Upgrade rust/crates/codespan-reporting to 0.11.0

Test: make
Change-Id: Ic52ba5f9bbf0ad9dd0cb7293bcc451623c2d3e82
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json
index 4139f46..edbabaa 100644
--- a/.cargo_vcs_info.json
+++ b/.cargo_vcs_info.json
@@ -1,5 +1,5 @@
 {
   "git": {
-    "sha1": "50bceff36e169673b49bfffa1809bba6be9bd07f"
+    "sha1": "2f26ac696b796968db370bea505095abc17ecfa4"
   }
 }
diff --git a/Android.bp b/Android.bp
index 10cfe99..8e17a51 100644
--- a/Android.bp
+++ b/Android.bp
@@ -13,5 +13,5 @@
 }
 
 // dependent_library ["feature_list"]
-//   termcolor-1.1.0
-//   unicode-width-0.1.7 "default"
+//   termcolor-1.1.2
+//   unicode-width-0.1.8 "default"
diff --git a/CHANGELOG.md b/CHANGELOG.md
index abd9bb5..da91f22 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,12 +7,87 @@
 
 ## [Unreleased]
 
+## [0.11.0] - 2020-11-30
+
+There is now a [code of conduct](https://github.com/brendanzab/codespan/blob/master/CODE_OF_CONDUCT.md)
+and a [contributing guide](https://github.com/brendanzab/codespan/blob/master/CONTRIBUTING.md).
+
+Some versions were skipped to sync up with the `codespan-lsp` crate. The release
+process has been changed so this should not happen again.
+
+### Added
+
+-   If a label spans over multiple lines, not all lines are rendered.
+    The number of lines rendered at beginning and end is configurable separately.
+-   There is now a custom error type.
+-   There now is a medium rendering mode that is like the short rendering mode
+    but also shows notes from the diagnostic.
+-   `PartialEq` and `Eq` implementations for the `diagnostic::{Diagnostic, Label, Severity}` types.
+
+### Changed
+
+-   All errors now use the error type `codespan_reporting::file::Error`.
+    This type also replaces the custom error type for `codespan-lsp`.
+
+### Fixed
+
+-   Empty error codes are not rendered.
+-   The locus ("location of the diagnostic") is now computed so it is always at the first
+    primary label, or at the first secondary label if no primary labels are available.
+-   All `unwrap`s outside of tests and examples have been removed.
+-   Some internal improvements, including various code style improvements by using Clippy.
+-   Improved documentation, also mentioning how the ordering of labels is handled.
+
 ## [0.9.5] - 2020-06-24
 
 ### Changed
 
 -   Sections of source code that are marked with primary labels are now rendered
     using the primary highlight color.
+-   Tab stops are now rendered properly.
+
+    We used to just render `\t` characters in source snippets with the same
+    number of spaces. For example, when rendering with a tab width of `3` we
+    would print:
+    ```
+    warning: tab test
+      ┌─ tab_columns:1:2
+      │
+    1 │    hello
+      │    ^^^^^
+    2 │ ∙   hello
+      │     ^^^^^
+    3 │ ∙∙   hello
+      │      ^^^^^
+    4 │ ∙∙∙   hello
+      │       ^^^^^
+    5 │ ∙∙∙∙   hello
+      │        ^^^^^
+    6 │ ∙∙∙∙∙   hello
+      │         ^^^^^
+    7 │ ∙∙∙∙∙∙   hello
+      │          ^^^^^
+    ```
+    Now we properly take into account the column of the tab character:
+    ```
+    warning: tab test
+      ┌─ tab_columns:1:2
+      │
+    1 │    hello
+      │    ^^^^^
+    2 │ ∙  hello
+      │    ^^^^^
+    3 │ ∙∙ hello
+      │    ^^^^^
+    4 │ ∙∙∙   hello
+      │       ^^^^^
+    5 │ ∙∙∙∙  hello
+      │       ^^^^^
+    6 │ ∙∙∙∙∙ hello
+      │       ^^^^^
+    7 │ ∙∙∙∙∙∙   hello
+      │          ^^^^^
+    ```
 
 ## [0.9.4] - 2020-05-18
 
@@ -245,8 +320,9 @@
 ## [0.2.1] - 2019-02-26
 ## [0.2.0] - 2018-10-11
 
-[Unreleased]: https://github.com/brendanzab/codespan/compare/v0.9.5...HEAD
-[0.9.4]: https://github.com/brendanzab/codespan/compare/v0.9.4...v0.9.5
+[Unreleased]: https://github.com/brendanzab/codespan/compare/v0.11.0...HEAD
+[0.11.0]: https://github.com/brendanzab/codespan/compare/v0.9.5...v0.11.0
+[0.9.5]: https://github.com/brendanzab/codespan/compare/v0.9.4...v0.9.5
 [0.9.4]: https://github.com/brendanzab/codespan/compare/v0.9.3...v0.9.4
 [0.9.3]: https://github.com/brendanzab/codespan/compare/v0.9.2...v0.9.3
 [0.9.2]: https://github.com/brendanzab/codespan/compare/v0.9.1...v0.9.2
diff --git a/Cargo.lock b/Cargo.lock
index e41e6d0..c43523a 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -11,21 +11,9 @@
 
 [[package]]
 name = "anyhow"
-version = "1.0.31"
+version = "1.0.34"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "85bb70cc08ec97ca5450e6eba421deeea5f172c0fc61f78b5357b2a8e8be195f"
-
-[[package]]
-name = "arrayref"
-version = "0.3.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544"
-
-[[package]]
-name = "arrayvec"
-version = "0.5.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8"
+checksum = "bf8dcb5b4bbaa28653b647d8c77bd4ed40183b48882e130c1f1ffb73de069fd7"
 
 [[package]]
 name = "atty"
@@ -39,39 +27,16 @@
 ]
 
 [[package]]
-name = "autocfg"
-version = "1.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d"
-
-[[package]]
-name = "base64"
-version = "0.11.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7"
-
-[[package]]
 name = "bitflags"
 version = "1.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
 
 [[package]]
-name = "blake2b_simd"
-version = "0.5.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d8fb2d74254a3a0b5cac33ac9f8ed0e44aa50378d9dbb2e5d83bd21ed1dc2c8a"
-dependencies = [
- "arrayref",
- "arrayvec",
- "constant_time_eq",
-]
-
-[[package]]
 name = "cc"
-version = "1.0.54"
+version = "1.0.65"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7bbb73db36c1246e9034e307d0fba23f9a2e251faa47ade70c1bd252220c8311"
+checksum = "95752358c8f7552394baf48cd82695b345628ad3f170d607de3ca03b8dacca15"
 
 [[package]]
 name = "cfg-if"
@@ -80,10 +45,16 @@
 checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
 
 [[package]]
-name = "clap"
-version = "2.33.1"
+name = "cfg-if"
+version = "1.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bdfa80d47f954d53a35a64987ca1422f495b8d6483c0fe9f7117b36c2a792129"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "clap"
+version = "2.33.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002"
 dependencies = [
  "ansi_term",
  "atty",
@@ -95,20 +66,8 @@
 ]
 
 [[package]]
-name = "clicolors-control"
-version = "1.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "90082ee5dcdd64dc4e9e0d37fbf3ee325419e39c0092191e0393df65518f741e"
-dependencies = [
- "atty",
- "lazy_static",
- "libc",
- "winapi",
-]
-
-[[package]]
 name = "codespan-reporting"
-version = "0.9.5"
+version = "0.11.0"
 dependencies = [
  "anyhow",
  "insta",
@@ -124,11 +83,10 @@
 
 [[package]]
 name = "console"
-version = "0.10.3"
+version = "0.12.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2586208b33573b7f76ccfbe5adb076394c88deaf81b84d7213969805b0a952a7"
+checksum = "c0b1aacfaffdbff75be81c15a399b4bedf78aaefe840e8af1d299ac2ade885d2"
 dependencies = [
- "clicolors-control",
  "encode_unicode",
  "lazy_static",
  "libc",
@@ -138,23 +96,6 @@
 ]
 
 [[package]]
-name = "constant_time_eq"
-version = "0.1.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc"
-
-[[package]]
-name = "crossbeam-utils"
-version = "0.7.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8"
-dependencies = [
- "autocfg",
- "cfg-if",
- "lazy_static",
-]
-
-[[package]]
 name = "difference"
 version = "2.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -162,19 +103,19 @@
 
 [[package]]
 name = "dirs-next"
-version = "1.0.1"
+version = "1.0.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1cbcf9241d9e8d106295bd496bbe2e9cffd5fa098f2a8c9e2bbcbf09773c11a8"
+checksum = "cf36e65a80337bea855cd4ef9b8401ffce06a7baedf2e85ec467b1ac3f6e82b6"
 dependencies = [
- "cfg-if",
+ "cfg-if 1.0.0",
  "dirs-sys-next",
 ]
 
 [[package]]
 name = "dirs-sys-next"
-version = "0.1.0"
+version = "0.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9c60f7b8a8953926148223260454befb50c751d3c50e1c178c4fd1ace4083c9a"
+checksum = "99de365f605554ae33f115102a02057d4fc18b01f3284d6870be0938743cfe7d"
 dependencies = [
  "libc",
  "redox_users",
@@ -195,11 +136,11 @@
 
 [[package]]
 name = "getrandom"
-version = "0.1.14"
+version = "0.1.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb"
+checksum = "fc587bc0ec293155d5bfa6b9891ec18a1e330c234f896ea47fbada4cadbe47e6"
 dependencies = [
- "cfg-if",
+ "cfg-if 0.1.10",
  "libc",
  "wasi",
 ]
@@ -215,18 +156,18 @@
 
 [[package]]
 name = "hermit-abi"
-version = "0.1.14"
+version = "0.1.17"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b9586eedd4ce6b3c498bc3b4dd92fc9f11166aa908a914071953768066c67909"
+checksum = "5aca5565f760fb5b220e499d72710ed156fdb74e631659e99377d9ebfbd13ae8"
 dependencies = [
  "libc",
 ]
 
 [[package]]
 name = "insta"
-version = "0.16.0"
+version = "1.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8386e795fb3927131ea4cede203c529a333652eb6dc4ff29616b832b27e9b096"
+checksum = "863bf97e7130bf788f29a99bc4073735af6b8ecc3da6a39c23b3a688d2d3109a"
 dependencies = [
  "console",
  "difference",
@@ -250,9 +191,9 @@
 
 [[package]]
 name = "libc"
-version = "0.2.71"
+version = "0.2.80"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9457b06509d27052635f90d6466700c65095fdf75409b3fbdd903e988b886f49"
+checksum = "4d58d1b70b004888f764dfbf6a26a3b0342a1632d33968e4a179d8011c760614"
 
 [[package]]
 name = "linked-hash-map"
@@ -262,37 +203,36 @@
 
 [[package]]
 name = "log"
-version = "0.4.8"
+version = "0.4.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7"
+checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b"
 dependencies = [
- "cfg-if",
+ "cfg-if 0.1.10",
 ]
 
 [[package]]
 name = "memchr"
-version = "2.3.3"
+version = "2.3.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400"
+checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525"
 
 [[package]]
 name = "nix"
-version = "0.17.0"
+version = "0.18.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "50e4785f2c3b7589a0d0c1dd60285e1188adac4006e8abd6dd578e1567027363"
+checksum = "83450fe6a6142ddd95fb064b746083fc4ef1705fe81f64a64e1d4b39f54a1055"
 dependencies = [
  "bitflags",
  "cc",
- "cfg-if",
+ "cfg-if 0.1.10",
  "libc",
- "void",
 ]
 
 [[package]]
 name = "peg"
-version = "0.6.2"
+version = "0.6.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9075875c14bb21f25f11cad4b6ad2e4dd443b8fb83900b2fbdd6ebd744b82e97"
+checksum = "9f76678828272f177ac33b7e2ac2e3e73cc6c1cd1e3e387928aa69562fa51367"
 dependencies = [
  "peg-macros",
  "peg-runtime",
@@ -300,9 +240,9 @@
 
 [[package]]
 name = "peg-macros"
-version = "0.6.2"
+version = "0.6.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c24c165fd39e995246140cc78df55c56c6733ba87e6658cb3e197b8856c62852"
+checksum = "636d60acf97633e48d266d7415a9355d4389cea327a193f87df395d88cd2b14d"
 dependencies = [
  "peg-runtime",
  "proc-macro2",
@@ -311,15 +251,15 @@
 
 [[package]]
 name = "peg-runtime"
-version = "0.6.2"
+version = "0.6.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0c1a2897e69d986c7986747ebad425cf03746ec5e3e09bb3b2600f91301ba864"
+checksum = "9555b1514d2d99d78150d3c799d4c357a3e2c2a8062cd108e93a06d9057629c5"
 
 [[package]]
 name = "proc-macro-error"
-version = "1.0.2"
+version = "1.0.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "98e9e4b82e0ef281812565ea4751049f1bdcdfccda7d3f459f2e138a40c08678"
+checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
 dependencies = [
  "proc-macro-error-attr",
  "proc-macro2",
@@ -330,22 +270,20 @@
 
 [[package]]
 name = "proc-macro-error-attr"
-version = "1.0.2"
+version = "1.0.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4f5444ead4e9935abd7f27dc51f7e852a0569ac888096d5ec2499470794e2e53"
+checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn",
- "syn-mid",
  "version_check",
 ]
 
 [[package]]
 name = "proc-macro2"
-version = "1.0.18"
+version = "1.0.24"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "beae6331a816b1f65d04c45b078fd8e6c93e8071771f41b8163255bbd8d7c8fa"
+checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71"
 dependencies = [
  "unicode-xid",
 ]
@@ -361,40 +299,27 @@
 
 [[package]]
 name = "redox_syscall"
-version = "0.1.56"
+version = "0.1.57"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84"
+checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce"
 
 [[package]]
 name = "redox_users"
-version = "0.3.4"
+version = "0.3.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "09b23093265f8d200fa7b4c2c76297f47e681c655f6f1285a8780d6a022f7431"
+checksum = "de0737333e7a9502c789a36d7c7fa6092a49895d4faa31ca5df163857ded2e9d"
 dependencies = [
  "getrandom",
  "redox_syscall",
- "rust-argon2",
-]
-
-[[package]]
-name = "rust-argon2"
-version = "0.7.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2bc8af4bda8e1ff4932523b94d3dd20ee30a87232323eda55903ffd71d2fb017"
-dependencies = [
- "base64",
- "blake2b_simd",
- "constant_time_eq",
- "crossbeam-utils",
 ]
 
 [[package]]
 name = "rustyline"
-version = "6.2.0"
+version = "6.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3358c21cbbc1a751892528db4e1de4b7a2b6a73f001e215aaba97d712cfa9777"
+checksum = "6f0d5e7b0219a3eadd5439498525d4765c59b7c993ef0c12244865cd2d988413"
 dependencies = [
- "cfg-if",
+ "cfg-if 0.1.10",
  "dirs-next",
  "libc",
  "log",
@@ -421,18 +346,18 @@
 
 [[package]]
 name = "serde"
-version = "1.0.114"
+version = "1.0.117"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5317f7588f0a5078ee60ef675ef96735a1442132dc645eb1d12c018620ed8cd3"
+checksum = "b88fa983de7720629c9387e9f517353ed404164b1e482c970a90c1a4aaf7dc1a"
 dependencies = [
  "serde_derive",
 ]
 
 [[package]]
 name = "serde_derive"
-version = "1.0.114"
+version = "1.0.117"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2a0be94b04690fbaed37cddffc5c134bf537c8e3329d53e982fe04c374978f8e"
+checksum = "cbd1ae72adb44aab48f325a02444a5fc079349a8d804c1fc922aed3f7454c74e"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -441,9 +366,9 @@
 
 [[package]]
 name = "serde_json"
-version = "1.0.55"
+version = "1.0.59"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ec2c5d7e739bc07a3e73381a39d61fdb5f671c60c1df26a130690665803d8226"
+checksum = "dcac07dbffa1c65e7f816ab9eba78eb142c6d44410f4eeba1e26e4f5dfa56b95"
 dependencies = [
  "itoa",
  "ryu",
@@ -452,9 +377,9 @@
 
 [[package]]
 name = "serde_yaml"
-version = "0.8.13"
+version = "0.8.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ae3e2dd40a7cdc18ca80db804b7f461a39bb721160a85c9a1fa30134bf3c02a5"
+checksum = "f7baae0a99f1a324984bcdc5f0718384c1f69775f1c7eec8b859b71b443e3fd7"
 dependencies = [
  "dtoa",
  "linked-hash-map",
@@ -470,9 +395,9 @@
 
 [[package]]
 name = "structopt"
-version = "0.3.15"
+version = "0.3.20"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "de2f5e239ee807089b62adce73e48c625e0ed80df02c7ab3f068f5db5281065c"
+checksum = "126d630294ec449fae0b16f964e35bf3c74f940da9dca17ee9b905f7b3112eb8"
 dependencies = [
  "clap",
  "lazy_static",
@@ -481,9 +406,9 @@
 
 [[package]]
 name = "structopt-derive"
-version = "0.4.8"
+version = "0.4.13"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "510413f9de616762a4fbeab62509bf15c729603b72d7cd71280fbca431b1c118"
+checksum = "65e51c492f9e23a220534971ff5afc14037289de430e3c83f9daf6a1b6ae91e8"
 dependencies = [
  "heck",
  "proc-macro-error",
@@ -494,9 +419,9 @@
 
 [[package]]
 name = "syn"
-version = "1.0.33"
+version = "1.0.52"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e8d5d96e8cbb005d6959f119f773bfaebb5684296108fb32600c00cde305b2cd"
+checksum = "6c1e438504729046a5cfae47f97c30d6d083c7d91d94603efdae3477fc070d4c"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -504,30 +429,19 @@
 ]
 
 [[package]]
-name = "syn-mid"
-version = "0.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7be3539f6c128a931cf19dcee741c1af532c7fd387baa739c03dd2e96479338a"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
-]
-
-[[package]]
 name = "termcolor"
-version = "1.1.0"
+version = "1.1.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bb6bfa289a4d7c5766392812c0a1f4c1ba45afa1ad47803c11e1f407d846d75f"
+checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4"
 dependencies = [
  "winapi-util",
 ]
 
 [[package]]
 name = "terminal_size"
-version = "0.1.12"
+version = "0.1.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8038f95fc7a6f351163f4b964af631bd26c9e828f7db085f2a84aca56f70d13b"
+checksum = "4bd2d183bd3fac5f5fe38ddbeb4dc9aec4a39a9d7d59e7491d900302da01cbe1"
 dependencies = [
  "libc",
  "winapi",
@@ -535,9 +449,9 @@
 
 [[package]]
 name = "termios"
-version = "0.3.2"
+version = "0.3.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6f0fcee7b24a25675de40d5bb4de6e41b0df07bc9856295e7e2b3a3600c400c2"
+checksum = "411c5bf740737c7918b8b1fe232dca4dc9f8e754b8ad5e20966814001ed0ac6b"
 dependencies = [
  "libc",
 ]
@@ -553,27 +467,27 @@
 
 [[package]]
 name = "unicode-segmentation"
-version = "1.6.0"
+version = "1.7.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0"
+checksum = "bb0d2e7be6ae3a5fa87eed5fb451aff96f2573d2694942e40543ae0bbe19c796"
 
 [[package]]
 name = "unicode-width"
-version = "0.1.7"
+version = "0.1.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "caaa9d531767d1ff2150b9332433f32a24622147e5ebb1f26409d5da67afd479"
+checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3"
 
 [[package]]
 name = "unicode-xid"
-version = "0.2.0"
+version = "0.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c"
+checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
 
 [[package]]
 name = "unindent"
-version = "0.1.6"
+version = "0.1.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "af41d708427f8fd0e915dcebb2cae0f0e6acb2a939b2d399c265c39a38a18942"
+checksum = "f14ee04d9415b52b3aeab06258a3f07093182b88ba0f9b8d203f211a7a7d41c7"
 
 [[package]]
 name = "utf8parse"
@@ -594,12 +508,6 @@
 checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed"
 
 [[package]]
-name = "void"
-version = "1.0.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
-
-[[package]]
 name = "wasi"
 version = "0.9.0+wasi-snapshot-preview1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -607,9 +515,9 @@
 
 [[package]]
 name = "winapi"
-version = "0.3.8"
+version = "0.3.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6"
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
 dependencies = [
  "winapi-i686-pc-windows-gnu",
  "winapi-x86_64-pc-windows-gnu",
diff --git a/Cargo.toml b/Cargo.toml
index 294d127..9a7fcd1 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -13,7 +13,7 @@
 [package]
 edition = "2018"
 name = "codespan-reporting"
-version = "0.9.5"
+version = "0.11.0"
 authors = ["Brendan Zabarauskas <[email protected]>"]
 exclude = ["assets/**"]
 description = "Beautiful diagnostic reporting for text-based programming languages"
@@ -36,7 +36,7 @@
 version = "1"
 
 [dev-dependencies.insta]
-version = "0.16"
+version = "1.0"
 
 [dev-dependencies.lazy_static]
 version = "1.4"
diff --git a/Cargo.toml.orig b/Cargo.toml.orig
index 427e854..0a6f79e 100644
--- a/Cargo.toml.orig
+++ b/Cargo.toml.orig
@@ -1,6 +1,6 @@
 [package]
 name = "codespan-reporting"
-version = "0.9.5"
+version = "0.11.0"
 readme = "../README.md"
 license = "Apache-2.0"
 authors = ["Brendan Zabarauskas <[email protected]>"]
@@ -18,7 +18,7 @@
 
 [dev-dependencies]
 anyhow = "1"
-insta = "0.16"
+insta = "1.0"
 lazy_static = "1.4"
 peg = "0.6"
 rustyline = "6"
diff --git a/METADATA b/METADATA
index b8b9d82..612e190 100644
--- a/METADATA
+++ b/METADATA
@@ -7,13 +7,13 @@
   }
   url {
     type: ARCHIVE
-    value: "https://static.crates.io/crates/codespan-reporting/codespan-reporting-0.9.5.crate"
+    value: "https://static.crates.io/crates/codespan-reporting/codespan-reporting-0.11.0.crate"
   }
-  version: "0.9.5"
+  version: "0.11.0"
   license_type: NOTICE
   last_upgrade_date {
     year: 2020
-    month: 7
-    day: 10
+    month: 11
+    day: 30
   }
 }
diff --git a/examples/custom_files.rs b/examples/custom_files.rs
index 2edfaa9..dbbacda 100644
--- a/examples/custom_files.rs
+++ b/examples/custom_files.rs
@@ -30,7 +30,7 @@
     ];
 
     let writer = StandardStream::stderr(ColorChoice::Always);
-    let config = codespan_reporting::term::Config::default();
+    let config = term::Config::default();
     for message in &messages {
         let writer = &mut writer.lock();
         term::emit(writer, &config, &files, &message.to_diagnostic())?;
@@ -56,13 +56,20 @@
     }
 
     impl File {
-        fn line_start(&self, line_index: usize) -> Option<usize> {
+        fn line_start(&self, line_index: usize) -> Result<usize, files::Error> {
             use std::cmp::Ordering;
 
             match line_index.cmp(&self.line_starts.len()) {
-                Ordering::Less => self.line_starts.get(line_index).cloned(),
-                Ordering::Equal => Some(self.source.len()),
-                Ordering::Greater => None,
+                Ordering::Less => Ok(self
+                    .line_starts
+                    .get(line_index)
+                    .expect("failed despite previous check")
+                    .clone()),
+                Ordering::Equal => Ok(self.source.len()),
+                Ordering::Greater => Err(files::Error::LineTooLarge {
+                    given: line_index,
+                    max: self.line_starts.len() - 1,
+                }),
             }
         }
     }
@@ -106,8 +113,10 @@
         }
 
         /// Get the file corresponding to the given id.
-        fn get(&self, file_id: FileId) -> Option<&File> {
-            self.files.get(file_id.0 as usize)
+        fn get(&self, file_id: FileId) -> Result<&File, files::Error> {
+            self.files
+                .get(file_id.0 as usize)
+                .ok_or(files::Error::FileMissing)
         }
     }
 
@@ -116,27 +125,31 @@
         type Name = &'files str;
         type Source = &'files str;
 
-        fn name(&self, file_id: FileId) -> Option<&str> {
-            Some(self.get(file_id)?.name.as_ref())
+        fn name(&self, file_id: FileId) -> Result<&str, files::Error> {
+            Ok(self.get(file_id)?.name.as_ref())
         }
 
-        fn source(&self, file_id: FileId) -> Option<&str> {
-            Some(&self.get(file_id)?.source)
+        fn source(&self, file_id: FileId) -> Result<&str, files::Error> {
+            Ok(&self.get(file_id)?.source)
         }
 
-        fn line_index(&self, file_id: FileId, byte_index: usize) -> Option<usize> {
-            match self.get(file_id)?.line_starts.binary_search(&byte_index) {
-                Ok(line) => Some(line),
-                Err(next_line) => Some(next_line - 1),
-            }
+        fn line_index(&self, file_id: FileId, byte_index: usize) -> Result<usize, files::Error> {
+            self.get(file_id)?
+                .line_starts
+                .binary_search(&byte_index)
+                .or_else(|next_line| Ok(next_line - 1))
         }
 
-        fn line_range(&self, file_id: FileId, line_index: usize) -> Option<Range<usize>> {
+        fn line_range(
+            &self,
+            file_id: FileId,
+            line_index: usize,
+        ) -> Result<Range<usize>, files::Error> {
             let file = self.get(file_id)?;
             let line_start = file.line_start(line_index)?;
             let next_line_start = file.line_start(line_index + 1)?;
 
-            Some(line_start..next_line_start)
+            Ok(line_start..next_line_start)
         }
     }
 }
diff --git a/examples/readme_preview.rs b/examples/readme_preview.rs
index aa3d8cf..2cf96f6 100644
--- a/examples/readme_preview.rs
+++ b/examples/readme_preview.rs
@@ -81,7 +81,10 @@
         Opts::Svg => {
             let mut buffer = Vec::new();
             let mut writer = HtmlEscapeWriter::new(SvgWriter::new(&mut buffer));
-            let config = codespan_reporting::term::Config::default();
+            let config = codespan_reporting::term::Config {
+                styles: codespan_reporting::term::Styles::with_blue(Color::Blue),
+                ..codespan_reporting::term::Config::default()
+            };
 
             for diagnostic in &diagnostics {
                 term::emit(&mut writer, &config, &file, &diagnostic)?;
diff --git a/src/diagnostic.rs b/src/diagnostic.rs
index 559c899..7dddcec 100644
--- a/src/diagnostic.rs
+++ b/src/diagnostic.rs
@@ -16,7 +16,7 @@
 /// assert!(Severity::Warning > Severity::Note);
 /// assert!(Severity::Note > Severity::Help);
 /// ```
-#[derive(Copy, Clone, PartialEq, Hash, Debug)]
+#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
 #[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
 pub enum Severity {
     /// An unexpected bug.
@@ -50,7 +50,7 @@
     }
 }
 
-#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd)]
 #[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
 pub enum LabelStyle {
     /// Labels that describe the primary cause of a diagnostic.
@@ -60,7 +60,7 @@
 }
 
 /// A label describing an underlined region of code associated with a diagnostic.
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, PartialEq, Eq)]
 #[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
 pub struct Label<FileId> {
     /// The style of the label.
@@ -112,7 +112,9 @@
 
 /// Represents a diagnostic message that can provide information like errors and
 /// warnings to the user.
-#[derive(Clone, Debug)]
+///
+/// The position of a Diagnostic is considered to be the position of the [`Label`] that has the earliest starting position and has the highest style which appears in all the labels of the diagnostic.
+#[derive(Clone, Debug, PartialEq, Eq)]
 #[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
 pub struct Diagnostic<FileId> {
     /// The overall severity of the diagnostic
@@ -126,6 +128,8 @@
     /// sense on its own, without additional context provided by labels and notes.
     pub message: String,
     /// Source labels that describe the cause of the diagnostic.
+    /// The order of the labels inside the vector does not have any meaning.
+    /// The labels are always arranged in the order they appear in the source code.
     pub labels: Vec<Label<FileId>>,
     /// Notes that are associated with the primary cause of the diagnostic.
     /// These can include line breaks for improved formatting.
diff --git a/src/files.rs b/src/files.rs
index 41691b9..b25cd79 100644
--- a/src/files.rs
+++ b/src/files.rs
@@ -7,7 +7,7 @@
 //! Simple implementations of this trait are implemented:
 //!
 //! - [`SimpleFile`]: For single-file use-cases
-//! - [`SimpleFiles`]: For single-file use-cases
+//! - [`SimpleFiles`]: For multi-file use-cases
 //!
 //! These data structures provide a pretty minimal API, however,
 //! so end-users are encouraged to create their own implementations for their
@@ -25,6 +25,58 @@
 
 use std::ops::Range;
 
+/// An enum representing an error that happened while looking up a file or a piece of content in that file.
+#[derive(Debug)]
+#[non_exhaustive]
+pub enum Error {
+    /// A required file is not in the file database.
+    FileMissing,
+    /// The file is present, but does not contain the specified byte index.
+    IndexTooLarge { given: usize, max: usize },
+    /// The file is present, but does not contain the specified line index.
+    LineTooLarge { given: usize, max: usize },
+    /// The file is present and contains the specified line index, but the line does not contain the specified column index.
+    ColumnTooLarge { given: usize, max: usize },
+    /// The given index is contained in the file, but is not a boundary of a UTF-8 code point.
+    InvalidCharBoundary { given: usize },
+    /// There was a error while doing IO.
+    Io(std::io::Error),
+}
+
+impl From<std::io::Error> for Error {
+    fn from(err: std::io::Error) -> Error {
+        Error::Io(err)
+    }
+}
+
+impl std::fmt::Display for Error {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        match self {
+            Error::FileMissing => write!(f, "file missing"),
+            Error::IndexTooLarge { given, max } => {
+                write!(f, "invalid index {}, maximum index is {}", given, max)
+            }
+            Error::LineTooLarge { given, max } => {
+                write!(f, "invalid line {}, maximum line is {}", given, max)
+            }
+            Error::ColumnTooLarge { given, max } => {
+                write!(f, "invalid column {}, maximum column {}", given, max)
+            }
+            Error::InvalidCharBoundary { .. } => write!(f, "index is not a code point boundary"),
+            Error::Io(err) => write!(f, "{}", err),
+        }
+    }
+}
+
+impl std::error::Error for Error {
+    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
+        match &self {
+            Error::Io(err) => Some(err),
+            _ => None,
+        }
+    }
+}
+
 /// A minimal interface for accessing source files when rendering diagnostics.
 ///
 /// A lifetime parameter `'a` is provided to allow any of the returned values to returned by reference.
@@ -40,12 +92,14 @@
     type Source: 'a + AsRef<str>;
 
     /// The user-facing name of a file.
-    fn name(&'a self, id: Self::FileId) -> Option<Self::Name>;
+    fn name(&'a self, id: Self::FileId) -> Result<Self::Name, Error>;
 
     /// The source code of a file.
-    fn source(&'a self, id: Self::FileId) -> Option<Self::Source>;
+    fn source(&'a self, id: Self::FileId) -> Result<Self::Source, Error>;
 
     /// The index of the line at the given byte index.
+    /// If the byte index is past the end of the file, returns the maximum line index in the file.
+    /// This means that this function only fails if the file is not present.
     ///
     /// # Note for trait implementors
     ///
@@ -56,9 +110,11 @@
     ///
     /// [`line_starts`]: crate::files::line_starts
     /// [`files`]: crate::files
-    fn line_index(&'a self, id: Self::FileId, byte_index: usize) -> Option<usize>;
+    fn line_index(&'a self, id: Self::FileId, byte_index: usize) -> Result<usize, Error>;
 
     /// The user-facing line number at the given line index.
+    /// It is not necessarily checked that the specified line index
+    /// is actually in the file.
     ///
     /// # Note for trait implementors
     ///
@@ -68,8 +124,8 @@
     ///
     /// [line-macro]: https://en.cppreference.com/w/c/preprocessor/line
     #[allow(unused_variables)]
-    fn line_number(&'a self, id: Self::FileId, line_index: usize) -> Option<usize> {
-        Some(line_index + 1)
+    fn line_number(&'a self, id: Self::FileId, line_index: usize) -> Result<usize, Error> {
+        Ok(line_index + 1)
     }
 
     /// The user-facing column number at the given line index and byte index.
@@ -87,27 +143,27 @@
         id: Self::FileId,
         line_index: usize,
         byte_index: usize,
-    ) -> Option<usize> {
+    ) -> Result<usize, Error> {
         let source = self.source(id)?;
         let line_range = self.line_range(id, line_index)?;
         let column_index = column_index(source.as_ref(), line_range, byte_index);
 
-        Some(column_index + 1)
+        Ok(column_index + 1)
     }
 
-    /// Convenience method for returning line and column number at the given a
+    /// Convenience method for returning line and column number at the given
     /// byte index in the file.
-    fn location(&'a self, id: Self::FileId, byte_index: usize) -> Option<Location> {
+    fn location(&'a self, id: Self::FileId, byte_index: usize) -> Result<Location, Error> {
         let line_index = self.line_index(id, byte_index)?;
 
-        Some(Location {
+        Ok(Location {
             line_number: self.line_number(id, line_index)?,
             column_number: self.column_number(id, line_index, byte_index)?,
         })
     }
 
     /// The byte range of line in the source of the file.
-    fn line_range(&'a self, id: Self::FileId, line_index: usize) -> Option<Range<usize>>;
+    fn line_range(&'a self, id: Self::FileId, line_index: usize) -> Result<Range<usize>, Error>;
 }
 
 /// A user-facing location in a source file.
@@ -115,7 +171,7 @@
 /// Returned by [`Files::location`].
 ///
 /// [`Files::location`]: Files::location
-#[derive(Debug, Copy, Clone)]
+#[derive(Debug, Copy, Clone, PartialEq, Eq)]
 pub struct Location {
     /// The user-facing line number.
     pub line_number: usize,
@@ -238,13 +294,22 @@
         &self.source
     }
 
-    fn line_start(&self, line_index: usize) -> Option<usize> {
+    /// Return the starting byte index of the line with the specified line index.
+    /// Convenience method that already generates errors if necessary.
+    fn line_start(&self, line_index: usize) -> Result<usize, Error> {
         use std::cmp::Ordering;
 
         match line_index.cmp(&self.line_starts.len()) {
-            Ordering::Less => self.line_starts.get(line_index).cloned(),
-            Ordering::Equal => Some(self.source.as_ref().len()),
-            Ordering::Greater => None,
+            Ordering::Less => Ok(self
+                .line_starts
+                .get(line_index)
+                .cloned()
+                .expect("failed despite previous check")),
+            Ordering::Equal => Ok(self.source.as_ref().len()),
+            Ordering::Greater => Err(Error::LineTooLarge {
+                given: line_index,
+                max: self.line_starts.len() - 1,
+            }),
         }
     }
 }
@@ -258,26 +323,26 @@
     type Name = Name;
     type Source = &'a str;
 
-    fn name(&self, (): ()) -> Option<Name> {
-        Some(self.name.clone())
+    fn name(&self, (): ()) -> Result<Name, Error> {
+        Ok(self.name.clone())
     }
 
-    fn source(&self, (): ()) -> Option<&str> {
-        Some(self.source.as_ref())
+    fn source(&self, (): ()) -> Result<&str, Error> {
+        Ok(self.source.as_ref())
     }
 
-    fn line_index(&self, (): (), byte_index: usize) -> Option<usize> {
-        match self.line_starts.binary_search(&byte_index) {
-            Ok(line) => Some(line),
-            Err(next_line) => Some(next_line - 1),
-        }
+    fn line_index(&self, (): (), byte_index: usize) -> Result<usize, Error> {
+        Ok(self
+            .line_starts
+            .binary_search(&byte_index)
+            .unwrap_or_else(|next_line| next_line - 1))
     }
 
-    fn line_range(&self, (): (), line_index: usize) -> Option<Range<usize>> {
+    fn line_range(&self, (): (), line_index: usize) -> Result<Range<usize>, Error> {
         let line_start = self.line_start(line_index)?;
         let next_line_start = self.line_start(line_index + 1)?;
 
-        Some(line_start..next_line_start)
+        Ok(line_start..next_line_start)
     }
 }
 
@@ -285,6 +350,7 @@
 ///
 /// This is useful for simple language tests, but it might be worth creating a
 /// custom implementation when a language scales beyond a certain size.
+/// It is a glorified `Vec<SimpleFile>` that implements the `Files` trait.
 #[derive(Debug, Clone)]
 pub struct SimpleFiles<Name, Source> {
     files: Vec<SimpleFile<Name, Source>>,
@@ -309,8 +375,8 @@
     }
 
     /// Get the file corresponding to the given id.
-    pub fn get(&self, file_id: usize) -> Option<&SimpleFile<Name, Source>> {
-        self.files.get(file_id)
+    pub fn get(&self, file_id: usize) -> Result<&SimpleFile<Name, Source>, Error> {
+        self.files.get(file_id).ok_or(Error::FileMissing)
     }
 }
 
@@ -323,19 +389,19 @@
     type Name = Name;
     type Source = &'a str;
 
-    fn name(&self, file_id: usize) -> Option<Name> {
-        Some(self.get(file_id)?.name().clone())
+    fn name(&self, file_id: usize) -> Result<Name, Error> {
+        Ok(self.get(file_id)?.name().clone())
     }
 
-    fn source(&self, file_id: usize) -> Option<&str> {
-        Some(self.get(file_id)?.source().as_ref())
+    fn source(&self, file_id: usize) -> Result<&str, Error> {
+        Ok(self.get(file_id)?.source().as_ref())
     }
 
-    fn line_index(&self, file_id: usize, byte_index: usize) -> Option<usize> {
+    fn line_index(&self, file_id: usize, byte_index: usize) -> Result<usize, Error> {
         self.get(file_id)?.line_index((), byte_index)
     }
 
-    fn line_range(&self, file_id: usize, line_index: usize) -> Option<Range<usize>> {
+    fn line_range(&self, file_id: usize, line_index: usize) -> Result<Range<usize>, Error> {
         self.get(file_id)?.line_range((), line_index)
     }
 }
diff --git a/src/term.rs b/src/term.rs
index c852e70..59baeb0 100644
--- a/src/term.rs
+++ b/src/term.rs
@@ -1,6 +1,5 @@
 //! Terminal back-end for emitting diagnostics.
 
-use std::io;
 use std::str::FromStr;
 use termcolor::{ColorChoice, WriteColor};
 
@@ -42,10 +41,8 @@
 ///     pub color: ColorArg,
 /// }
 ///
-/// fn main() {
-///     let opts = Opts::from_args();
-///     let writer = StandardStream::stderr(opts.color.into());
-/// }
+/// let opts = Opts::from_args();
+/// let writer = StandardStream::stderr(opts.color.into());
 /// ```
 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
 pub struct ColorArg(pub ColorChoice);
@@ -82,19 +79,25 @@
 }
 
 /// Emit a diagnostic using the given writer, context, config, and files.
+///
+/// The return value covers all error cases. These error case can arise if:
+/// * a file was removed from the file database.
+/// * a file was changed so that it is too small to have an index
+/// * IO fails
 pub fn emit<'files, F: Files<'files>>(
     writer: &mut dyn WriteColor,
     config: &Config,
     files: &'files F,
     diagnostic: &Diagnostic<F::FileId>,
-) -> io::Result<()> {
+) -> Result<(), super::files::Error> {
     use self::renderer::Renderer;
     use self::views::{RichDiagnostic, ShortDiagnostic};
 
     let mut renderer = Renderer::new(writer, config);
     match config.display_style {
-        DisplayStyle::Rich => RichDiagnostic::new(diagnostic).render(files, &mut renderer),
-        DisplayStyle::Short => ShortDiagnostic::new(diagnostic).render(files, &mut renderer),
+        DisplayStyle::Rich => RichDiagnostic::new(diagnostic, config).render(files, &mut renderer),
+        DisplayStyle::Medium => ShortDiagnostic::new(diagnostic, true).render(files, &mut renderer),
+        DisplayStyle::Short => ShortDiagnostic::new(diagnostic, false).render(files, &mut renderer),
     }
 }
 
diff --git a/src/term/config.rs b/src/term/config.rs
index 7b834d7..18cbd44 100644
--- a/src/term/config.rs
+++ b/src/term/config.rs
@@ -17,6 +17,14 @@
     pub styles: Styles,
     /// Characters to use when rendering the diagnostic.
     pub chars: Chars,
+    /// The minimum number of lines to be shown after the line on which a multiline [`Label`] begins.
+    ///
+    /// Defaults to: `3`.
+    pub start_context_lines: usize,
+    /// The minimum number of lines to be shown before the line on which a multiline [`Label`] ends.
+    ///
+    /// Defaults to: `1`.
+    pub end_context_lines: usize,
 }
 
 impl Default for Config {
@@ -26,6 +34,8 @@
             tab_width: 4,
             styles: Styles::default(),
             chars: Chars::default(),
+            start_context_lines: 3,
+            end_context_lines: 1,
         }
     }
 }
@@ -49,6 +59,16 @@
     ///
     /// ```
     Rich,
+    /// Output a condensed diagnostic, with a line number, severity, message and notes (if any).
+    ///
+    /// ```text
+    /// test:2:9: error[E0001]: unexpected type in `+` application
+    /// = expected type `Int`
+    ///      found type `String`
+    ///
+    /// error[E0002]: Bad config found
+    /// ```
+    Medium,
     /// Output a short diagnostic, with a line number, severity, and message.
     ///
     /// ```text
diff --git a/src/term/renderer.rs b/src/term/renderer.rs
index ab5656e..cd82e88 100644
--- a/src/term/renderer.rs
+++ b/src/term/renderer.rs
@@ -1,9 +1,9 @@
 use std::io::{self, Write};
-use std::ops::{Range, RangeTo};
+use std::ops::Range;
 use termcolor::{ColorSpec, WriteColor};
 
 use crate::diagnostic::{LabelStyle, Severity};
-use crate::files::Location;
+use crate::files::{Error, Location};
 use crate::term::{Chars, Config, Styles};
 
 /// The 'location focus' of a source code snippet.
@@ -23,20 +23,22 @@
 
 /// A multi-line label to render.
 ///
-/// Locations are relative to the start of where the source cord is rendered.
+/// Locations are relative to the start of where the source code is rendered.
 pub enum MultiLabel<'diagnostic> {
-    /// Left top corner for multi-line labels.
-    ///
-    /// ```text
-    /// ╭
-    /// ```
-    TopLeft,
     /// Multi-line label top.
+    /// The contained value indicates where the label starts.
     ///
     /// ```text
     /// ╭────────────^
     /// ```
-    Top(RangeTo<usize>),
+    ///
+    /// Can also be rendered at the beginning of the line
+    /// if there is only whitespace before the label starts.
+    ///
+    /// /// ```text
+    /// ╭
+    /// ```
+    Top(usize),
     /// Left vertical labels for multi-line labels.
     ///
     /// ```text
@@ -44,11 +46,12 @@
     /// ```
     Left,
     /// Multi-line label bottom, with an optional message.
+    /// The first value indicates where the label ends.
     ///
     /// ```text
     /// ╰────────────^ blah blah
     /// ```
-    Bottom(RangeTo<usize>, &'diagnostic str),
+    Bottom(usize, &'diagnostic str),
 }
 
 #[derive(Copy, Clone)]
@@ -138,7 +141,7 @@
         severity: Severity,
         code: Option<&str>,
         message: &str,
-    ) -> io::Result<()> {
+    ) -> Result<(), Error> {
         // Write locus
         //
         // ```text
@@ -168,7 +171,7 @@
         // ```text
         // [E0001]
         // ```
-        if let Some(code) = &code {
+        if let Some(code) = &code.filter(|code| !code.is_empty()) {
             write!(self, "[{}]", code)?;
         }
 
@@ -181,15 +184,14 @@
         write!(self, ": {}", message)?;
         self.reset()?;
 
-        write!(self, "\n")?;
+        writeln!(self)?;
 
         Ok(())
     }
 
     /// Empty line.
-    pub fn render_empty(&mut self) -> io::Result<()> {
-        write!(self, "\n")?;
-
+    pub fn render_empty(&mut self) -> Result<(), Error> {
+        writeln!(self)?;
         Ok(())
     }
 
@@ -198,7 +200,11 @@
     /// ```text
     /// ┌─ test:2:9
     /// ```
-    pub fn render_snippet_start(&mut self, outer_padding: usize, locus: &Locus) -> io::Result<()> {
+    pub fn render_snippet_start(
+        &mut self,
+        outer_padding: usize,
+        locus: &Locus,
+    ) -> Result<(), Error> {
         self.outer_gutter(outer_padding)?;
 
         self.set_color(&self.styles().source_border)?;
@@ -209,7 +215,7 @@
         write!(self, " ")?;
         self.snippet_locus(&locus)?;
 
-        write!(self, "\n")?;
+        writeln!(self)?;
 
         Ok(())
     }
@@ -229,7 +235,7 @@
         single_labels: &[SingleLabel<'_>],
         num_multi_labels: usize,
         multi_labels: &[(usize, LabelStyle, MultiLabel<'_>)],
-    ) -> io::Result<()> {
+    ) -> Result<(), Error> {
         // Trim trailing newlines, linefeeds, and null chars from source, if they exist.
         // FIXME: Use the number of trimmed placeholders when rendering single line carets
         let source = source.trim_end_matches(['\n', '\r', '\0'].as_ref());
@@ -250,7 +256,9 @@
                 match multi_labels_iter.peek() {
                     Some((label_index, label_style, label)) if *label_index == label_column => {
                         match label {
-                            MultiLabel::TopLeft => {
+                            MultiLabel::Top(start)
+                                if *start <= source.len() - source.trim_start().len() =>
+                            {
                                 self.label_multi_top_left(severity, *label_style)?;
                             }
                             MultiLabel::Top(..) => self.inner_gutter_space()?,
@@ -276,9 +284,9 @@
                 }) || multi_labels.iter().any(|(_, ls, label)| {
                     *ls == LabelStyle::Primary
                         && match label {
-                            MultiLabel::Top(range) => column_range.start >= range.end,
-                            MultiLabel::TopLeft | MultiLabel::Left => true,
-                            MultiLabel::Bottom(range, _) => column_range.end <= range.end,
+                            MultiLabel::Top(start) => column_range.start >= *start,
+                            MultiLabel::Left => true,
+                            MultiLabel::Bottom(start, _) => column_range.end <= *start,
                         }
                 });
 
@@ -299,7 +307,7 @@
             if in_primary {
                 self.reset()?;
             }
-            write!(self, "\n")?;
+            writeln!(self)?;
         }
 
         // Write single labels underneath source
@@ -456,7 +464,7 @@
                 write!(self, "{}", message)?;
                 self.reset()?;
             }
-            write!(self, "\n")?;
+            writeln!(self)?;
 
             // Write hanging labels pointing to carets
             //
@@ -483,7 +491,7 @@
                     trailing_label,
                     source.char_indices(),
                 )?;
-                write!(self, "\n")?;
+                writeln!(self)?;
 
                 // Write hanging labels pointing to carets
                 //
@@ -511,7 +519,7 @@
                     self.set_color(self.styles().label(severity, *label_style))?;
                     write!(self, "{}", message)?;
                     self.reset()?;
-                    write!(self, "\n")?;
+                    writeln!(self)?;
                 }
             }
         }
@@ -524,7 +532,11 @@
         // ```
         for (multi_label_index, (_, label_style, label)) in multi_labels.iter().enumerate() {
             let (label_style, range, bottom_message) = match label {
-                MultiLabel::TopLeft | MultiLabel::Left => continue, // no label caret needed
+                MultiLabel::Left => continue, // no label caret needed
+                // no label caret needed if this can be started in front of the line
+                MultiLabel::Top(start) if *start <= source.len() - source.trim_start().len() => {
+                    continue
+                }
                 MultiLabel::Top(range) => (*label_style, range, None),
                 MultiLabel::Bottom(range, message) => (*label_style, range, Some(message)),
             };
@@ -543,7 +555,7 @@
                 match multi_labels_iter.peek() {
                     Some((i, (label_index, ls, label))) if *label_index == label_column => {
                         match label {
-                            MultiLabel::TopLeft | MultiLabel::Left => {
+                            MultiLabel::Left => {
                                 self.label_multi_left(severity, *ls, underline.map(|(s, _)| s))?;
                             }
                             MultiLabel::Top(..) if multi_label_index > *i => {
@@ -571,11 +583,10 @@
             }
 
             // Finish the top or bottom caret
-            let range = range.clone();
             match bottom_message {
-                None => self.label_multi_top_caret(severity, label_style, source, range)?,
+                None => self.label_multi_top_caret(severity, label_style, source, *range)?,
                 Some(message) => {
-                    self.label_multi_bottom_caret(severity, label_style, source, range, message)?
+                    self.label_multi_bottom_caret(severity, label_style, source, *range, message)?
                 }
             }
         }
@@ -594,11 +605,11 @@
         severity: Severity,
         num_multi_labels: usize,
         multi_labels: &[(usize, LabelStyle, MultiLabel<'_>)],
-    ) -> io::Result<()> {
+    ) -> Result<(), Error> {
         self.outer_gutter(outer_padding)?;
         self.border_left()?;
         self.inner_gutter(severity, num_multi_labels, multi_labels)?;
-        write!(self, "\n")?;
+        writeln!(self)?;
         Ok(())
     }
 
@@ -613,11 +624,11 @@
         severity: Severity,
         num_multi_labels: usize,
         multi_labels: &[(usize, LabelStyle, MultiLabel<'_>)],
-    ) -> io::Result<()> {
+    ) -> Result<(), Error> {
         self.outer_gutter(outer_padding)?;
         self.border_left_break()?;
         self.inner_gutter(severity, num_multi_labels, multi_labels)?;
-        write!(self, "\n")?;
+        writeln!(self)?;
         Ok(())
     }
 
@@ -627,7 +638,11 @@
     /// = expected type `Int`
     ///      found type `String`
     /// ```
-    pub fn render_snippet_note(&mut self, outer_padding: usize, message: &str) -> io::Result<()> {
+    pub fn render_snippet_note(
+        &mut self,
+        outer_padding: usize,
+        message: &str,
+    ) -> Result<(), Error> {
         for (note_line_index, line) in message.lines().enumerate() {
             self.outer_gutter(outer_padding)?;
             match note_line_index {
@@ -639,8 +654,7 @@
                 _ => write!(self, " ")?,
             }
             // Write line of message
-            write!(self, " {}", line)?;
-            write!(self, "\n")?;
+            writeln!(self, " {}", line)?;
         }
 
         Ok(())
@@ -674,25 +688,29 @@
     }
 
     /// Location focus.
-    fn snippet_locus(&mut self, locus: &Locus) -> io::Result<()> {
+    fn snippet_locus(&mut self, locus: &Locus) -> Result<(), Error> {
         write!(
             self,
             "{name}:{line_number}:{column_number}",
             name = locus.name,
             line_number = locus.location.line_number,
             column_number = locus.location.column_number,
-        )
+        )?;
+        Ok(())
     }
 
     /// The outer gutter of a source line.
-    fn outer_gutter(&mut self, outer_padding: usize) -> io::Result<()> {
-        write!(self, "{space: >width$}", space = "", width = outer_padding)?;
-        write!(self, " ")?;
+    fn outer_gutter(&mut self, outer_padding: usize) -> Result<(), Error> {
+        write!(self, "{space: >width$} ", space = "", width = outer_padding)?;
         Ok(())
     }
 
     /// The outer gutter of a source line, with line number.
-    fn outer_gutter_number(&mut self, line_number: usize, outer_padding: usize) -> io::Result<()> {
+    fn outer_gutter_number(
+        &mut self,
+        line_number: usize,
+        outer_padding: usize,
+    ) -> Result<(), Error> {
         self.set_color(&self.styles().line_number)?;
         write!(
             self,
@@ -706,7 +724,7 @@
     }
 
     /// The left-hand border of a source line.
-    fn border_left(&mut self) -> io::Result<()> {
+    fn border_left(&mut self) -> Result<(), Error> {
         self.set_color(&self.styles().source_border)?;
         write!(self, "{}", self.chars().source_border_left)?;
         self.reset()?;
@@ -714,7 +732,7 @@
     }
 
     /// The broken left-hand border of a source line.
-    fn border_left_break(&mut self) -> io::Result<()> {
+    fn border_left_break(&mut self) -> Result<(), Error> {
         self.set_color(&self.styles().source_border)?;
         write!(self, "{}", self.chars().source_border_left_break)?;
         self.reset()?;
@@ -729,7 +747,7 @@
         single_labels: &[SingleLabel<'_>],
         trailing_label: Option<(usize, &SingleLabel<'_>)>,
         char_indices: impl Iterator<Item = (usize, char)>,
-    ) -> io::Result<()> {
+    ) -> Result<(), Error> {
         for (metrics, ch) in self.char_metrics(char_indices) {
             let column_range = metrics.byte_index..(metrics.byte_index + ch.len_utf8());
             let label_style = hanging_labels(single_labels, trailing_label)
@@ -765,7 +783,7 @@
         severity: Severity,
         label_style: LabelStyle,
         underline: Option<LabelStyle>,
-    ) -> io::Result<()> {
+    ) -> Result<(), Error> {
         match underline {
             None => write!(self, " ")?,
             // Continue an underline horizontally
@@ -790,7 +808,7 @@
         &mut self,
         severity: Severity,
         label_style: LabelStyle,
-    ) -> io::Result<()> {
+    ) -> Result<(), Error> {
         write!(self, " ")?;
         self.set_color(self.styles().label(severity, label_style))?;
         write!(self, "{}", self.chars().multi_top_left)?;
@@ -807,7 +825,7 @@
         &mut self,
         severity: Severity,
         label_style: LabelStyle,
-    ) -> io::Result<()> {
+    ) -> Result<(), Error> {
         write!(self, " ")?;
         self.set_color(self.styles().label(severity, label_style))?;
         write!(self, "{}", self.chars().multi_bottom_left)?;
@@ -825,13 +843,13 @@
         severity: Severity,
         label_style: LabelStyle,
         source: &str,
-        range: RangeTo<usize>,
-    ) -> io::Result<()> {
+        start: usize,
+    ) -> Result<(), Error> {
         self.set_color(self.styles().label(severity, label_style))?;
 
         for (metrics, _) in self
             .char_metrics(source.char_indices())
-            .take_while(|(metrics, _)| metrics.byte_index < range.end + 1)
+            .take_while(|(metrics, _)| metrics.byte_index < start + 1)
         {
             // FIXME: improve rendering of carets between character boundaries
             (0..metrics.unicode_width)
@@ -844,7 +862,7 @@
         };
         write!(self, "{}", caret_start)?;
         self.reset()?;
-        write!(self, "\n")?;
+        writeln!(self)?;
         Ok(())
     }
 
@@ -858,14 +876,14 @@
         severity: Severity,
         label_style: LabelStyle,
         source: &str,
-        range: RangeTo<usize>,
+        start: usize,
         message: &str,
-    ) -> io::Result<()> {
+    ) -> Result<(), Error> {
         self.set_color(self.styles().label(severity, label_style))?;
 
         for (metrics, _) in self
             .char_metrics(source.char_indices())
-            .take_while(|(metrics, _)| metrics.byte_index < range.end)
+            .take_while(|(metrics, _)| metrics.byte_index < start)
         {
             // FIXME: improve rendering of carets between character boundaries
             (0..metrics.unicode_width)
@@ -881,7 +899,7 @@
             write!(self, " {}", message)?;
         }
         self.reset()?;
-        write!(self, "\n")?;
+        writeln!(self)?;
         Ok(())
     }
 
@@ -890,7 +908,7 @@
         &mut self,
         severity: Severity,
         underline: Option<Underline>,
-    ) -> io::Result<()> {
+    ) -> Result<(), Error> {
         match underline {
             None => self.inner_gutter_space(),
             Some((label_style, vertical_bound)) => {
@@ -907,8 +925,9 @@
     }
 
     /// Writes an empty gutter space.
-    fn inner_gutter_space(&mut self) -> io::Result<()> {
-        write!(self, "  ")
+    fn inner_gutter_space(&mut self) -> Result<(), Error> {
+        write!(self, "  ")?;
+        Ok(())
     }
 
     /// Writes an inner gutter, with the left lines if necessary.
@@ -917,12 +936,12 @@
         severity: Severity,
         num_multi_labels: usize,
         multi_labels: &[(usize, LabelStyle, MultiLabel<'_>)],
-    ) -> io::Result<()> {
+    ) -> Result<(), Error> {
         let mut multi_labels_iter = multi_labels.iter().peekable();
         for label_column in 0..num_multi_labels {
             match multi_labels_iter.peek() {
                 Some((label_index, ls, label)) if *label_index == label_column => match label {
-                    MultiLabel::TopLeft | MultiLabel::Left | MultiLabel::Bottom(..) => {
+                    MultiLabel::Left | MultiLabel::Bottom(..) => {
                         self.label_multi_left(severity, *ls, None)?;
                         multi_labels_iter.next();
                     }
diff --git a/src/term/views.rs b/src/term/views.rs
index aa79914..f09d958 100644
--- a/src/term/views.rs
+++ b/src/term/views.rs
@@ -1,9 +1,9 @@
-use std::io;
 use std::ops::Range;
 
 use crate::diagnostic::{Diagnostic, LabelStyle};
-use crate::files::{Files, Location};
+use crate::files::{Error, Files, Location};
 use crate::term::renderer::{Locus, MultiLabel, Renderer, SingleLabel};
+use crate::term::Config;
 
 /// Count the number of decimal digits in `n`.
 fn count_digits(mut n: usize) -> usize {
@@ -16,23 +16,27 @@
 }
 
 /// Output a richly formatted diagnostic, with source code previews.
-pub struct RichDiagnostic<'diagnostic, FileId> {
+pub struct RichDiagnostic<'diagnostic, 'config, FileId> {
     diagnostic: &'diagnostic Diagnostic<FileId>,
+    config: &'config Config,
 }
 
-impl<'diagnostic, FileId> RichDiagnostic<'diagnostic, FileId>
+impl<'diagnostic, 'config, FileId> RichDiagnostic<'diagnostic, 'config, FileId>
 where
     FileId: Copy + PartialEq,
 {
-    pub fn new(diagnostic: &'diagnostic Diagnostic<FileId>) -> RichDiagnostic<'diagnostic, FileId> {
-        RichDiagnostic { diagnostic }
+    pub fn new(
+        diagnostic: &'diagnostic Diagnostic<FileId>,
+        config: &'config Config,
+    ) -> RichDiagnostic<'diagnostic, 'config, FileId> {
+        RichDiagnostic { diagnostic, config }
     }
 
     pub fn render<'files>(
         &self,
         files: &'files impl Files<'files, FileId = FileId>,
         renderer: &mut Renderer<'_, '_>,
-    ) -> io::Result<()>
+    ) -> Result<(), Error>
     where
         FileId: 'files,
     {
@@ -45,6 +49,7 @@
             location: Location,
             num_multi_labels: usize,
             lines: BTreeMap<usize, Line<'diagnostic>>,
+            max_label_style: LabelStyle,
         }
 
         impl<'diagnostic, FileId> LabeledFile<'diagnostic, FileId> {
@@ -59,6 +64,8 @@
                     number: line_number,
                     single_labels: vec![],
                     multi_labels: vec![],
+                    // This has to be false by default so we know if it must be rendered by another condition already.
+                    must_render: false,
                 })
             }
         }
@@ -69,6 +76,7 @@
             // TODO: How do we reuse these allocations?
             single_labels: Vec<SingleLabel<'diagnostic>>,
             multi_labels: Vec<(usize, LabelStyle, MultiLabel<'diagnostic>)>,
+            must_render: bool,
         }
 
         // TODO: Make this data structure external, to allow for allocation reuse
@@ -79,15 +87,12 @@
 
         // Group labels by file
         for label in &self.diagnostic.labels {
-            let source = files.source(label.file_id).unwrap();
-            let source = source.as_ref();
-
-            let start_line_index = files.line_index(label.file_id, label.range.start).unwrap();
-            let start_line_number = files.line_number(label.file_id, start_line_index).unwrap();
-            let start_line_range = files.line_range(label.file_id, start_line_index).unwrap();
-            let end_line_index = files.line_index(label.file_id, label.range.end).unwrap();
-            let end_line_number = files.line_number(label.file_id, end_line_index).unwrap();
-            let end_line_range = files.line_range(label.file_id, end_line_index).unwrap();
+            let start_line_index = files.line_index(label.file_id, label.range.start)?;
+            let start_line_number = files.line_number(label.file_id, start_line_index)?;
+            let start_line_range = files.line_range(label.file_id, start_line_index)?;
+            let end_line_index = files.line_index(label.file_id, label.range.end)?;
+            let end_line_number = files.line_number(label.file_id, end_line_index)?;
+            let end_line_range = files.line_range(label.file_id, end_line_index)?;
 
             outer_padding = std::cmp::max(outer_padding, count_digits(start_line_number));
             outer_padding = std::cmp::max(outer_padding, count_digits(end_line_number));
@@ -100,23 +105,33 @@
                 .find(|labeled_file| label.file_id == labeled_file.file_id)
             {
                 Some(labeled_file) => {
-                    if labeled_file.start > label.range.start {
+                    // another diagnostic also referenced this file
+                    if labeled_file.max_label_style > label.style
+                        || (labeled_file.max_label_style == label.style
+                            && labeled_file.start > label.range.start)
+                    {
+                        // this label has a higher style or has the same style but starts earlier
                         labeled_file.start = label.range.start;
-                        labeled_file.location =
-                            files.location(label.file_id, label.range.start).unwrap();
+                        labeled_file.location = files.location(label.file_id, label.range.start)?;
+                        labeled_file.max_label_style = label.style;
                     }
                     labeled_file
                 }
                 None => {
+                    // no other diagnostic referenced this file yet
                     labeled_files.push(LabeledFile {
                         file_id: label.file_id,
                         start: label.range.start,
-                        name: files.name(label.file_id).unwrap().to_string(),
-                        location: files.location(label.file_id, label.range.start).unwrap(),
+                        name: files.name(label.file_id)?.to_string(),
+                        location: files.location(label.file_id, label.range.start)?,
                         num_multi_labels: 0,
                         lines: BTreeMap::new(),
+                        max_label_style: label.style,
                     });
-                    labeled_files.last_mut().unwrap()
+                    // this unwrap should never fail because we just pushed an element
+                    labeled_files
+                        .last_mut()
+                        .expect("just pushed an element that disappeared")
                 }
             };
 
@@ -128,7 +143,10 @@
                 //   │         ^^ expected `Int` but found `String`
                 // ```
                 let label_start = label.range.start - start_line_range.start;
-                let label_end = label.range.end - start_line_range.start;
+                // Ensure that we print at least one caret, even when we
+                // have a zero-length source range.
+                let label_end =
+                    usize::max(label.range.end - start_line_range.start, label_start + 1);
 
                 let line = labeled_file.get_or_insert_line(
                     start_line_index,
@@ -149,15 +167,11 @@
                     Ok(index) | Err(index) => index,
                 };
 
-                // Ensure that we print at least one caret, even when we
-                // have a zero-length source range.
-                let mut label_range = label_start..label_end;
-                if label_range.len() == 0 {
-                    label_range.end = label_range.start + 1;
-                }
-
                 line.single_labels
-                    .insert(index, (label.style, label_range, &label.message));
+                    .insert(index, (label.style, label_start..label_end, &label.message));
+
+                // If this line is not rendered, the SingleLabel is not visible.
+                line.must_render = true;
             } else {
                 // Multiple lines
                 //
@@ -176,29 +190,21 @@
 
                 // First labeled line
                 let label_start = label.range.start - start_line_range.start;
-                let prefix_source = &source[start_line_range.start..label.range.start];
 
-                labeled_file
-                    .get_or_insert_line(start_line_index, start_line_range, start_line_number)
-                    .multi_labels
-                    // TODO: Do this in the `Renderer`?
-                    .push(match prefix_source.trim() {
-                        // Section is prefixed by empty space, so we don't need to take
-                        // up a new line.
-                        //
-                        // ```text
-                        // 4 │ ╭     case (mod num 5) (mod num 3) of
-                        // ```
-                        "" => (label_index, label.style, MultiLabel::TopLeft),
-                        // There's source code in the prefix, so run a label
-                        // underneath it to get to the start of the range.
-                        //
-                        // ```text
-                        // 4 │   fizz₁ num = case (mod num 5) (mod num 3) of
-                        //   │ ╭─────────────^
-                        // ```
-                        _ => (label_index, label.style, MultiLabel::Top(..label_start)),
-                    });
+                let start_line = labeled_file.get_or_insert_line(
+                    start_line_index,
+                    start_line_range.clone(),
+                    start_line_number,
+                );
+
+                start_line.multi_labels.push((
+                    label_index,
+                    label.style,
+                    MultiLabel::Top(label_start),
+                ));
+
+                // The first line has to be rendered so the start of the label is visible.
+                start_line.must_render = true;
 
                 // Marked lines
                 //
@@ -207,17 +213,24 @@
                 // 6 │ │     0 _ => "Fizz"
                 // 7 │ │     _ 0 => "Buzz"
                 // ```
-                // TODO(#125): If start line and end line are too far apart, add a source break.
                 for line_index in (start_line_index + 1)..end_line_index {
-                    let line_range = files.line_range(label.file_id, line_index).unwrap();
-                    let line_number = files.line_number(label.file_id, line_index).unwrap();
+                    let line_range = files.line_range(label.file_id, line_index)?;
+                    let line_number = files.line_number(label.file_id, line_index)?;
 
                     outer_padding = std::cmp::max(outer_padding, count_digits(line_number));
 
-                    labeled_file
-                        .get_or_insert_line(line_index, line_range, line_number)
-                        .multi_labels
+                    let line = labeled_file.get_or_insert_line(line_index, line_range, line_number);
+
+                    line.multi_labels
                         .push((label_index, label.style, MultiLabel::Left));
+
+                    // The line should be rendered to match the configuration of how much context to show.
+                    line.must_render |=
+                        // Is this line part of the context after the start of the label?
+                        line_index - start_line_index <= self.config.start_context_lines
+                        ||
+                        // Is this line part of the context before the end of the label?
+                        end_line_index - line_index <= self.config.end_context_lines;
                 }
 
                 // Last labeled line
@@ -228,14 +241,20 @@
                 // ```
                 let label_end = label.range.end - end_line_range.start;
 
-                labeled_file
-                    .get_or_insert_line(end_line_index, end_line_range, end_line_number)
-                    .multi_labels
-                    .push((
-                        label_index,
-                        label.style,
-                        MultiLabel::Bottom(..label_end, &label.message),
-                    ));
+                let end_line = labeled_file.get_or_insert_line(
+                    end_line_index,
+                    end_line_range,
+                    end_line_number,
+                );
+
+                end_line.multi_labels.push((
+                    label_index,
+                    label.style,
+                    MultiLabel::Bottom(label_end, &label.message),
+                ));
+
+                // The last line has to be rendered so the end of the label is visible.
+                end_line.must_render = true;
             }
         }
 
@@ -247,7 +266,7 @@
         renderer.render_header(
             None,
             self.diagnostic.severity,
-            self.diagnostic.code.as_ref().map(String::as_str),
+            self.diagnostic.code.as_deref(),
             self.diagnostic.message.as_str(),
         )?;
 
@@ -262,7 +281,7 @@
         // ```
         let mut labeled_files = labeled_files.into_iter().peekable();
         while let Some(labeled_file) = labeled_files.next() {
-            let source = files.source(labeled_file.file_id).unwrap();
+            let source = files.source(labeled_file.file_id)?;
             let source = source.as_ref();
 
             // Top left border and locus.
@@ -286,8 +305,11 @@
                 )?;
             }
 
-            let mut lines = labeled_file.lines.into_iter().peekable();
-            let current_labels = Vec::new();
+            let mut lines = labeled_file
+                .lines
+                .iter()
+                .filter(|(_, line)| line.must_render)
+                .peekable();
 
             while let Some((line_index, line)) = lines.next() {
                 renderer.render_snippet_source(
@@ -303,21 +325,29 @@
                 // Check to see if we need to render any intermediate stuff
                 // before rendering the next line.
                 if let Some((next_line_index, _)) = lines.peek() {
-                    match next_line_index.checked_sub(line_index) {
+                    match next_line_index.checked_sub(*line_index) {
                         // Consecutive lines
                         Some(1) => {}
                         // One line between the current line and the next line
                         Some(2) => {
                             // Write a source line
                             let file_id = labeled_file.file_id;
+
+                            // This line was not intended to be rendered initially.
+                            // To render the line right, we have to get back the original labels.
+                            let labels = labeled_file
+                                .lines
+                                .get(&(line_index + 1))
+                                .map_or(&[][..], |line| &line.multi_labels[..]);
+
                             renderer.render_snippet_source(
                                 outer_padding,
-                                files.line_number(file_id, line_index + 1).unwrap(),
-                                &source[files.line_range(file_id, line_index + 1).unwrap()],
+                                files.line_number(file_id, line_index + 1)?,
+                                &source[files.line_range(file_id, line_index + 1)?],
                                 self.diagnostic.severity,
                                 &[],
                                 labeled_file.num_multi_labels,
-                                &current_labels,
+                                labels,
                             )?;
                         }
                         // More than one line between the current line and the next line.
@@ -331,7 +361,7 @@
                                 outer_padding,
                                 self.diagnostic.severity,
                                 labeled_file.num_multi_labels,
-                                &current_labels,
+                                &line.multi_labels,
                             )?;
                         }
                     }
@@ -350,7 +380,7 @@
                     outer_padding,
                     self.diagnostic.severity,
                     labeled_file.num_multi_labels,
-                    &current_labels,
+                    &[],
                 )?;
             }
         }
@@ -364,15 +394,14 @@
         for note in &self.diagnostic.notes {
             renderer.render_snippet_note(outer_padding, note)?;
         }
-        renderer.render_empty()?;
-
-        Ok(())
+        renderer.render_empty()
     }
 }
 
 /// Output a short diagnostic, with a line number, severity, and message.
 pub struct ShortDiagnostic<'diagnostic, FileId> {
     diagnostic: &'diagnostic Diagnostic<FileId>,
+    show_notes: bool,
 }
 
 impl<'diagnostic, FileId> ShortDiagnostic<'diagnostic, FileId>
@@ -381,15 +410,19 @@
 {
     pub fn new(
         diagnostic: &'diagnostic Diagnostic<FileId>,
+        show_notes: bool,
     ) -> ShortDiagnostic<'diagnostic, FileId> {
-        ShortDiagnostic { diagnostic }
+        ShortDiagnostic {
+            diagnostic,
+            show_notes,
+        }
     }
 
     pub fn render<'files>(
         &self,
         files: &'files impl Files<'files, FileId = FileId>,
         renderer: &mut Renderer<'_, '_>,
-    ) -> io::Result<()>
+    ) -> Result<(), Error>
     where
         FileId: 'files,
     {
@@ -405,11 +438,11 @@
 
             renderer.render_header(
                 Some(&Locus {
-                    name: files.name(label.file_id).unwrap().to_string(),
-                    location: files.location(label.file_id, label.range.start).unwrap(),
+                    name: files.name(label.file_id)?.to_string(),
+                    location: files.location(label.file_id, label.range.start)?,
                 }),
                 self.diagnostic.severity,
-                self.diagnostic.code.as_ref().map(String::as_str),
+                self.diagnostic.code.as_deref(),
                 self.diagnostic.message.as_str(),
             )?;
         }
@@ -423,11 +456,23 @@
             renderer.render_header(
                 None,
                 self.diagnostic.severity,
-                self.diagnostic.code.as_ref().map(String::as_str),
+                self.diagnostic.code.as_deref(),
                 self.diagnostic.message.as_str(),
             )?;
         }
 
+        if self.show_notes {
+            // Additional notes
+            //
+            // ```text
+            // = expected type `Int`
+            //      found type `String`
+            // ```
+            for note in &self.diagnostic.notes {
+                renderer.render_snippet_note(0, note)?;
+            }
+        }
+
         Ok(())
     }
 }
diff --git a/tests/snapshots/term__empty__medium_color.snap b/tests/snapshots/term__empty__medium_color.snap
new file mode 100644
index 0000000..e4a14fb
--- /dev/null
+++ b/tests/snapshots/term__empty__medium_color.snap
@@ -0,0 +1,11 @@
+---
+source: codespan-reporting/tests/term.rs
+expression: TEST_DATA.emit_color(&config)
+---
+{fg:Red bold bright}bug{bold bright}: {/}
+{fg:Red bold bright}error{bold bright}: {/}
+{fg:Yellow bold bright}warning{bold bright}: {/}
+{fg:Green bold bright}note{bold bright}: {/}
+{fg:Cyan bold bright}help{bold bright}: {/}
+{fg:Red bold bright}bug{bold bright}: {/}
+
diff --git a/tests/snapshots/term__empty__medium_no_color.snap b/tests/snapshots/term__empty__medium_no_color.snap
new file mode 100644
index 0000000..6a8bf45
--- /dev/null
+++ b/tests/snapshots/term__empty__medium_no_color.snap
@@ -0,0 +1,11 @@
+---
+source: codespan-reporting/tests/term.rs
+expression: TEST_DATA.emit_no_color(&config)
+---
+bug: 
+error: 
+warning: 
+note: 
+help: 
+bug: 
+
diff --git a/tests/snapshots/term__empty_ranges__medium_color.snap b/tests/snapshots/term__empty_ranges__medium_color.snap
new file mode 100644
index 0000000..3713ee4
--- /dev/null
+++ b/tests/snapshots/term__empty_ranges__medium_color.snap
@@ -0,0 +1,9 @@
+---
+source: codespan-reporting/tests/term.rs
+expression: TEST_DATA.emit_color(&config)
+---
+hello:1:7: {fg:Green bold bright}note{bold bright}: middle{/}
+hello:1:13: {fg:Green bold bright}note{bold bright}: end of line{/}
+hello:2:11: {fg:Green bold bright}note{bold bright}: end of line{/}
+hello:3:4: {fg:Green bold bright}note{bold bright}: end of file{/}
+
diff --git a/tests/snapshots/term__empty_ranges__medium_no_color.snap b/tests/snapshots/term__empty_ranges__medium_no_color.snap
new file mode 100644
index 0000000..635a830
--- /dev/null
+++ b/tests/snapshots/term__empty_ranges__medium_no_color.snap
@@ -0,0 +1,9 @@
+---
+source: codespan-reporting/tests/term.rs
+expression: TEST_DATA.emit_no_color(&config)
+---
+hello:1:7: note: middle
+hello:1:13: note: end of line
+hello:2:11: note: end of line
+hello:3:4: note: end of file
+
diff --git a/tests/snapshots/term__fizz_buzz__medium_color.snap b/tests/snapshots/term__fizz_buzz__medium_color.snap
new file mode 100644
index 0000000..ef97c2c
--- /dev/null
+++ b/tests/snapshots/term__fizz_buzz__medium_color.snap
@@ -0,0 +1,11 @@
+---
+source: codespan-reporting/tests/term.rs
+expression: TEST_DATA.emit_color(&config)
+---
+FizzBuzz.fun:8:12: {fg:Red bold bright}error[E0308]{bold bright}: `case` clauses have incompatible types{/}
+ {fg:Blue}={/} expected type `String`
+      found type `Nat`
+FizzBuzz.fun:16:16: {fg:Red bold bright}error[E0308]{bold bright}: `case` clauses have incompatible types{/}
+ {fg:Blue}={/} expected type `String`
+      found type `Nat`
+
diff --git a/tests/snapshots/term__fizz_buzz__medium_no_color.snap b/tests/snapshots/term__fizz_buzz__medium_no_color.snap
new file mode 100644
index 0000000..1120064
--- /dev/null
+++ b/tests/snapshots/term__fizz_buzz__medium_no_color.snap
@@ -0,0 +1,11 @@
+---
+source: codespan-reporting/tests/term.rs
+expression: TEST_DATA.emit_no_color(&config)
+---
+FizzBuzz.fun:8:12: error[E0308]: `case` clauses have incompatible types
+ = expected type `String`
+      found type `Nat`
+FizzBuzz.fun:16:16: error[E0308]: `case` clauses have incompatible types
+ = expected type `String`
+      found type `Nat`
+
diff --git a/tests/snapshots/term__fizz_buzz__rich_color.snap b/tests/snapshots/term__fizz_buzz__rich_color.snap
index a051eef..79dded2 100644
--- a/tests/snapshots/term__fizz_buzz__rich_color.snap
+++ b/tests/snapshots/term__fizz_buzz__rich_color.snap
@@ -3,7 +3,7 @@
 expression: TEST_DATA.emit_color(&config)
 ---
 {fg:Red bold bright}error[E0308]{bold bright}: `case` clauses have incompatible types{/}
-  {fg:Blue}┌─{/} FizzBuzz.fun:3:15
+  {fg:Blue}┌─{/} FizzBuzz.fun:8:12
   {fg:Blue}│{/}  
 {fg:Blue}3{/} {fg:Blue}│{/}   fizz₁ : Nat → String
   {fg:Blue}│{/}                 {fg:Blue}------{/} {fg:Blue}expected type `String` found here{/}
@@ -20,7 +20,7 @@
        found type `Nat`
 
 {fg:Red bold bright}error[E0308]{bold bright}: `case` clauses have incompatible types{/}
-   {fg:Blue}┌─{/} FizzBuzz.fun:10:15
+   {fg:Blue}┌─{/} FizzBuzz.fun:16:16
    {fg:Blue}│{/}  
 {fg:Blue}10{/} {fg:Blue}│{/}   fizz₂ : Nat → String
    {fg:Blue}│{/}                 {fg:Blue}------{/} {fg:Blue}expected type `String` found here{/}
diff --git a/tests/snapshots/term__fizz_buzz__rich_no_color.snap b/tests/snapshots/term__fizz_buzz__rich_no_color.snap
index dac0107..a0b7260 100644
--- a/tests/snapshots/term__fizz_buzz__rich_no_color.snap
+++ b/tests/snapshots/term__fizz_buzz__rich_no_color.snap
@@ -3,7 +3,7 @@
 expression: TEST_DATA.emit_no_color(&config)
 ---
 error[E0308]: `case` clauses have incompatible types
-  ┌─ FizzBuzz.fun:3:15
+  ┌─ FizzBuzz.fun:8:12

 3 │   fizz₁ : Nat → String
   │                 ------ expected type `String` found here
@@ -20,7 +20,7 @@
        found type `Nat`
 
 error[E0308]: `case` clauses have incompatible types
-   ┌─ FizzBuzz.fun:10:15
+   ┌─ FizzBuzz.fun:16:16

 10 │   fizz₂ : Nat → String
    │                 ------ expected type `String` found here
diff --git a/tests/snapshots/term__message__medium_color.snap b/tests/snapshots/term__message__medium_color.snap
new file mode 100644
index 0000000..f0b16d8
--- /dev/null
+++ b/tests/snapshots/term__message__medium_color.snap
@@ -0,0 +1,9 @@
+---
+source: codespan-reporting/tests/term.rs
+expression: TEST_DATA.emit_color(&config)
+---
+{fg:Red bold bright}error{bold bright}: a message{/}
+{fg:Yellow bold bright}warning{bold bright}: a message{/}
+{fg:Green bold bright}note{bold bright}: a message{/}
+{fg:Cyan bold bright}help{bold bright}: a message{/}
+
diff --git a/tests/snapshots/term__message__medium_no_color.snap b/tests/snapshots/term__message__medium_no_color.snap
new file mode 100644
index 0000000..43565ac
--- /dev/null
+++ b/tests/snapshots/term__message__medium_no_color.snap
@@ -0,0 +1,9 @@
+---
+source: codespan-reporting/tests/term.rs
+expression: TEST_DATA.emit_no_color(&config)
+---
+error: a message
+warning: a message
+note: a message
+help: a message
+
diff --git a/tests/snapshots/term__message_and_notes__medium_color.snap b/tests/snapshots/term__message_and_notes__medium_color.snap
new file mode 100644
index 0000000..f44b2a4
--- /dev/null
+++ b/tests/snapshots/term__message_and_notes__medium_color.snap
@@ -0,0 +1,13 @@
+---
+source: codespan-reporting/tests/term.rs
+expression: TEST_DATA.emit_color(&config)
+---
+{fg:Red bold bright}error{bold bright}: a message{/}
+ {fg:Blue}={/} a note
+{fg:Yellow bold bright}warning{bold bright}: a message{/}
+ {fg:Blue}={/} a note
+{fg:Green bold bright}note{bold bright}: a message{/}
+ {fg:Blue}={/} a note
+{fg:Cyan bold bright}help{bold bright}: a message{/}
+ {fg:Blue}={/} a note
+
diff --git a/tests/snapshots/term__message_and_notes__medium_no_color.snap b/tests/snapshots/term__message_and_notes__medium_no_color.snap
new file mode 100644
index 0000000..bab7a65
--- /dev/null
+++ b/tests/snapshots/term__message_and_notes__medium_no_color.snap
@@ -0,0 +1,13 @@
+---
+source: codespan-reporting/tests/term.rs
+expression: TEST_DATA.emit_no_color(&config)
+---
+error: a message
+ = a note
+warning: a message
+ = a note
+note: a message
+ = a note
+help: a message
+ = a note
+
diff --git a/tests/snapshots/term__message_errorcode__rich_no_color.snap b/tests/snapshots/term__message_errorcode__rich_no_color.snap
new file mode 100644
index 0000000..e82fb7c
--- /dev/null
+++ b/tests/snapshots/term__message_errorcode__rich_no_color.snap
@@ -0,0 +1,21 @@
+---
+source: codespan-reporting/tests/term.rs
+expression: TEST_DATA.emit_no_color(&config)
+---
+error[E0001]: a message
+
+warning[W001]: a message
+
+note[N0815]: a message
+
+help[H4711]: a message
+
+error: where did my errorcode go?
+
+warning: where did my errorcode go?
+
+note: where did my errorcode go?
+
+help: where did my errorcode go?
+
+
diff --git a/tests/snapshots/term__message_errorcode__short_no_color.snap b/tests/snapshots/term__message_errorcode__short_no_color.snap
new file mode 100644
index 0000000..ccddd8d
--- /dev/null
+++ b/tests/snapshots/term__message_errorcode__short_no_color.snap
@@ -0,0 +1,13 @@
+---
+source: codespan-reporting/tests/term.rs
+expression: TEST_DATA.emit_no_color(&config)
+---
+error[E0001]: a message
+warning[W001]: a message
+note[N0815]: a message
+help[H4711]: a message
+error: where did my errorcode go?
+warning: where did my errorcode go?
+note: where did my errorcode go?
+help: where did my errorcode go?
+
diff --git a/tests/snapshots/term__multifile__medium_color.snap b/tests/snapshots/term__multifile__medium_color.snap
new file mode 100644
index 0000000..6c03d83
--- /dev/null
+++ b/tests/snapshots/term__multifile__medium_color.snap
@@ -0,0 +1,12 @@
+---
+source: codespan-reporting/tests/term.rs
+expression: TEST_DATA.emit_color(&config)
+---
+Data/Nat.fun:7:13: {fg:Red bold bright}error{bold bright}: unknown builtin: `NATRAL`{/}
+ {fg:Blue}={/} there is a builtin with a similar name: `NATURAL`
+Data/Nat.fun:17:16: {fg:Yellow bold bright}warning{bold bright}: unused parameter pattern: `n₂`{/}
+ {fg:Blue}={/} consider using a wildcard pattern: `_`
+Test.fun:4:11: {fg:Red bold bright}error[E0001]{bold bright}: unexpected type in application of `_+_`{/}
+ {fg:Blue}={/} expected type `Nat`
+      found type `String`
+
diff --git a/tests/snapshots/term__multifile__medium_no_color.snap b/tests/snapshots/term__multifile__medium_no_color.snap
new file mode 100644
index 0000000..db33fc0
--- /dev/null
+++ b/tests/snapshots/term__multifile__medium_no_color.snap
@@ -0,0 +1,12 @@
+---
+source: codespan-reporting/tests/term.rs
+expression: TEST_DATA.emit_no_color(&config)
+---
+Data/Nat.fun:7:13: error: unknown builtin: `NATRAL`
+ = there is a builtin with a similar name: `NATURAL`
+Data/Nat.fun:17:16: warning: unused parameter pattern: `n₂`
+ = consider using a wildcard pattern: `_`
+Test.fun:4:11: error[E0001]: unexpected type in application of `_+_`
+ = expected type `Nat`
+      found type `String`
+
diff --git a/tests/snapshots/term__multiline_omit__rich_no_color.snap b/tests/snapshots/term__multiline_omit__rich_no_color.snap
new file mode 100644
index 0000000..bb9363d
--- /dev/null
+++ b/tests/snapshots/term__multiline_omit__rich_no_color.snap
@@ -0,0 +1,38 @@
+---
+source: codespan-reporting/tests/term.rs
+expression: TEST_DATA.emit_no_color(&config)
+---
+error[empty_if]: empty elseif block
+   ┌─ empty_if_comments.lua:1:1
+   │    
+ 1 │ ╭   elseif 3 then
+ 2 │ │   
+ 3 │ │ ╭ 
+ 4 │ │ │ 
+ 5 │ │ │ 
+   · │ │
+ 8 │ │ │ 
+ 9 │ │ │ 
+   │ │ ╰' content should be in here
+10 │ │   else
+   │ ╰───^
+
+error[E0308]: mismatched types
+   ┌─ src/lib.rs:2:6
+   │  
+ 2 │       1
+   │ ╭─────^
+ 3 │ │     + 1
+ 4 │ │     + 1
+   · │
+ 7 │ │     +1
+   │ │      - missing whitespace
+ 8 │ │     + 1
+ 9 │ │     + 1
+10 │ │     + 1
+   │ ╰───────^ expected (), found integer
+   │  
+   = note:	expected type `()`
+     	found type `{integer}`
+
+
diff --git a/tests/snapshots/term__multiline_overlapping__medium_color.snap b/tests/snapshots/term__multiline_overlapping__medium_color.snap
new file mode 100644
index 0000000..afddf24
--- /dev/null
+++ b/tests/snapshots/term__multiline_overlapping__medium_color.snap
@@ -0,0 +1,9 @@
+---
+source: codespan-reporting/tests/term.rs
+expression: TEST_DATA.emit_color(&config)
+---
+codespan/src/file.rs:4:34: {fg:Red bold bright}error[E0308]{bold bright}: match arms have incompatible types{/}
+ {fg:Blue}={/} expected type `Result<ByteIndex, LineIndexOutOfBoundsError>`
+      found type `LineIndexOutOfBoundsError`
+
+
diff --git a/tests/snapshots/term__multiline_overlapping__medium_no_color.snap b/tests/snapshots/term__multiline_overlapping__medium_no_color.snap
new file mode 100644
index 0000000..0e50bd8
--- /dev/null
+++ b/tests/snapshots/term__multiline_overlapping__medium_no_color.snap
@@ -0,0 +1,9 @@
+---
+source: codespan-reporting/tests/term.rs
+expression: TEST_DATA.emit_no_color(&config)
+---
+codespan/src/file.rs:4:34: error[E0308]: match arms have incompatible types
+ = expected type `Result<ByteIndex, LineIndexOutOfBoundsError>`
+      found type `LineIndexOutOfBoundsError`
+
+
diff --git a/tests/snapshots/term__multiline_overlapping__rich_color.snap b/tests/snapshots/term__multiline_overlapping__rich_color.snap
index fee20e1..10701fb 100644
--- a/tests/snapshots/term__multiline_overlapping__rich_color.snap
+++ b/tests/snapshots/term__multiline_overlapping__rich_color.snap
@@ -3,7 +3,7 @@
 expression: TEST_DATA.emit_color(&config)
 ---
 {fg:Red bold bright}error[E0308]{bold bright}: match arms have incompatible types{/}
-  {fg:Blue}┌─{/} codespan/src/file.rs:1:9
+  {fg:Blue}┌─{/} codespan/src/file.rs:4:34
   {fg:Blue}│{/}    
 {fg:Blue}1{/} {fg:Blue}│{/}   {fg:Blue}╭{/}         match line_index.compare(self.last_line_index()) {
 {fg:Blue}2{/} {fg:Blue}│{/}   {fg:Blue}│{/}             Ordering::Less => Ok(self.line_starts()[line_index.to_usize()]),
diff --git a/tests/snapshots/term__multiline_overlapping__rich_no_color.snap b/tests/snapshots/term__multiline_overlapping__rich_no_color.snap
index 8f82c15..f6802c8 100644
--- a/tests/snapshots/term__multiline_overlapping__rich_no_color.snap
+++ b/tests/snapshots/term__multiline_overlapping__rich_no_color.snap
@@ -3,7 +3,7 @@
 expression: TEST_DATA.emit_no_color(&config)
 ---
 error[E0308]: match arms have incompatible types
-  ┌─ codespan/src/file.rs:1:9
+  ┌─ codespan/src/file.rs:4:34

 1 │   ╭         match line_index.compare(self.last_line_index()) {
 2 │   │             Ordering::Less => Ok(self.line_starts()[line_index.to_usize()]),
diff --git a/tests/snapshots/term__overlapping__medium_color.snap b/tests/snapshots/term__overlapping__medium_color.snap
new file mode 100644
index 0000000..9ccf3e5
--- /dev/null
+++ b/tests/snapshots/term__overlapping__medium_color.snap
@@ -0,0 +1,17 @@
+---
+source: codespan-reporting/tests/term.rs
+expression: TEST_DATA.emit_color(&config)
+---
+nested_impl_trait.rs:5:56: {fg:Red bold bright}error[E0666]{bold bright}: nested `impl Trait` is not allowed{/}
+typeck_type_placeholder_item.rs:1:18: {fg:Red bold bright}error[E0121]{bold bright}: the type placeholder `_` is not allowed within types on item signatures{/}
+typeck_type_placeholder_item.rs:2:25: {fg:Red bold bright}error[E0121]{bold bright}: the type placeholder `_` is not allowed within types on item signatures{/}
+typeck_type_placeholder_item.rs:2:28: {fg:Red bold bright}error[E0121]{bold bright}: the type placeholder `_` is not allowed within types on item signatures{/}
+no_send_res_ports.rs:25:5: {fg:Red bold bright}error[E0277]{bold bright}: `std::rc::Rc<()>` cannot be sent between threads safely{/}
+ {fg:Blue}={/} help: within `[closure@no_send_res_ports.rs:29:19: 33:6 x:main::Foo]`, the trait `std::marker::Send` is not implemented for `std::rc::Rc<()>`
+ {fg:Blue}={/} note: required because it appears within the type `Port<()>`
+ {fg:Blue}={/} note: required because it appears within the type `main::Foo`
+ {fg:Blue}={/} note: required because it appears within the type `[closure@no_send_res_ports.rs:29:19: 33:6 x:main::Foo]`
+{fg:Red bold bright}error{bold bright}: aborting due 5 previous errors{/}
+ {fg:Blue}={/} Some errors have detailed explanations: E0121, E0277, E0666.
+ {fg:Blue}={/} For more information about an error, try `rustc --explain E0121`.
+
diff --git a/tests/snapshots/term__overlapping__medium_no_color.snap b/tests/snapshots/term__overlapping__medium_no_color.snap
new file mode 100644
index 0000000..3b9eac9
--- /dev/null
+++ b/tests/snapshots/term__overlapping__medium_no_color.snap
@@ -0,0 +1,17 @@
+---
+source: codespan-reporting/tests/term.rs
+expression: TEST_DATA.emit_no_color(&config)
+---
+nested_impl_trait.rs:5:56: error[E0666]: nested `impl Trait` is not allowed
+typeck_type_placeholder_item.rs:1:18: error[E0121]: the type placeholder `_` is not allowed within types on item signatures
+typeck_type_placeholder_item.rs:2:25: error[E0121]: the type placeholder `_` is not allowed within types on item signatures
+typeck_type_placeholder_item.rs:2:28: error[E0121]: the type placeholder `_` is not allowed within types on item signatures
+no_send_res_ports.rs:25:5: error[E0277]: `std::rc::Rc<()>` cannot be sent between threads safely
+ = help: within `[closure@no_send_res_ports.rs:29:19: 33:6 x:main::Foo]`, the trait `std::marker::Send` is not implemented for `std::rc::Rc<()>`
+ = note: required because it appears within the type `Port<()>`
+ = note: required because it appears within the type `main::Foo`
+ = note: required because it appears within the type `[closure@no_send_res_ports.rs:29:19: 33:6 x:main::Foo]`
+error: aborting due 5 previous errors
+ = Some errors have detailed explanations: E0121, E0277, E0666.
+ = For more information about an error, try `rustc --explain E0121`.
+
diff --git a/tests/snapshots/term__overlapping__rich_color.snap b/tests/snapshots/term__overlapping__rich_color.snap
index 92f061f..d29a429 100644
--- a/tests/snapshots/term__overlapping__rich_color.snap
+++ b/tests/snapshots/term__overlapping__rich_color.snap
@@ -3,7 +3,7 @@
 expression: TEST_DATA.emit_color(&config)
 ---
 {fg:Red bold bright}error[E0666]{bold bright}: nested `impl Trait` is not allowed{/}
-  {fg:Blue}┌─{/} nested_impl_trait.rs:5:46
+  {fg:Blue}┌─{/} nested_impl_trait.rs:5:56
   {fg:Blue}│{/}
 {fg:Blue}5{/} {fg:Blue}│{/} fn bad_in_ret_position(x: impl Into<u32>) -> impl Into<{fg:Red}impl Debug{/}> { x }
   {fg:Blue}│{/}                                              {fg:Blue}----------{fg:Red}^^^^^^^^^^{fg:Blue}-{/}
@@ -21,7 +21,7 @@
   {fg:Blue}│{/}                  {fg:Blue}help: replace with the correct return type: `i32`{/}
 
 {fg:Red bold bright}error[E0121]{bold bright}: the type placeholder `_` is not allowed within types on item signatures{/}
-  {fg:Blue}┌─{/} typeck_type_placeholder_item.rs:2:24
+  {fg:Blue}┌─{/} typeck_type_placeholder_item.rs:2:25
   {fg:Blue}│{/}
 {fg:Blue}2{/} {fg:Blue}│{/} fn fn_test2(x: i32) -> ({fg:Red}_{/}, {fg:Red}_{/}) { (x, x) }
   {fg:Blue}│{/}                        {fg:Blue}-{fg:Red}^{fg:Blue}--{fg:Red}^{fg:Blue}-{/}
diff --git a/tests/snapshots/term__overlapping__rich_no_color.snap b/tests/snapshots/term__overlapping__rich_no_color.snap
index e4a944d..32f8eec 100644
--- a/tests/snapshots/term__overlapping__rich_no_color.snap
+++ b/tests/snapshots/term__overlapping__rich_no_color.snap
@@ -3,7 +3,7 @@
 expression: TEST_DATA.emit_no_color(&config)
 ---
 error[E0666]: nested `impl Trait` is not allowed
-  ┌─ nested_impl_trait.rs:5:46
+  ┌─ nested_impl_trait.rs:5:56

 5 │ fn bad_in_ret_position(x: impl Into<u32>) -> impl Into<impl Debug> { x }
   │                                              ----------^^^^^^^^^^-
@@ -21,7 +21,7 @@
   │                  help: replace with the correct return type: `i32`
 
 error[E0121]: the type placeholder `_` is not allowed within types on item signatures
-  ┌─ typeck_type_placeholder_item.rs:2:24
+  ┌─ typeck_type_placeholder_item.rs:2:25

 2 │ fn fn_test2(x: i32) -> (_, _) { (x, x) }
   │                        -^--^-
diff --git a/tests/snapshots/term__position_indicator__medium_no_color.snap b/tests/snapshots/term__position_indicator__medium_no_color.snap
new file mode 100644
index 0000000..0e40c83
--- /dev/null
+++ b/tests/snapshots/term__position_indicator__medium_no_color.snap
@@ -0,0 +1,7 @@
+---
+source: codespan-reporting/tests/term.rs
+expression: TEST_DATA.emit_no_color(&config)
+---
+tests/main.js:4:3: warning[ParserWarning]: The strict mode declaration in the body of function `foo` is redundant, as the outer scope is already in strict mode
+
+
diff --git a/tests/snapshots/term__position_indicator__rich_no_color.snap b/tests/snapshots/term__position_indicator__rich_no_color.snap
new file mode 100644
index 0000000..35217e4
--- /dev/null
+++ b/tests/snapshots/term__position_indicator__rich_no_color.snap
@@ -0,0 +1,14 @@
+---
+source: codespan-reporting/tests/term.rs
+expression: TEST_DATA.emit_no_color(&config)
+---
+warning[ParserWarning]: The strict mode declaration in the body of function `foo` is redundant, as the outer scope is already in strict mode
+  ┌─ tests/main.js:4:3
+  │
+1 │ "use strict";
+  │ ------------ Strict mode is first declared here
+  ·
+4 │   "use strict";
+  │   ^^^^^^^^^^^^ This strict mode declaration is redundant
+
+
diff --git a/tests/snapshots/term__position_indicator__short_no_color.snap b/tests/snapshots/term__position_indicator__short_no_color.snap
new file mode 100644
index 0000000..3ec348d
--- /dev/null
+++ b/tests/snapshots/term__position_indicator__short_no_color.snap
@@ -0,0 +1,6 @@
+---
+source: codespan-reporting/tests/term.rs
+expression: TEST_DATA.emit_no_color(&config)
+---
+tests/main.js:4:3: warning[ParserWarning]: The strict mode declaration in the body of function `foo` is redundant, as the outer scope is already in strict mode
+
diff --git a/tests/snapshots/term__same_line__medium_color.snap b/tests/snapshots/term__same_line__medium_color.snap
new file mode 100644
index 0000000..43e1eb0
--- /dev/null
+++ b/tests/snapshots/term__same_line__medium_color.snap
@@ -0,0 +1,8 @@
+---
+source: codespan-reporting/tests/term.rs
+expression: TEST_DATA.emit_color(&config)
+---
+one_line.rs:3:12: {fg:Red bold bright}error[E0499]{bold bright}: cannot borrow `v` as mutable more than once at a time{/}
+{fg:Red bold bright}error{bold bright}: aborting due to previous error{/}
+ {fg:Blue}={/} For more information about this error, try `rustc --explain E0499`.
+
diff --git a/tests/snapshots/term__same_line__medium_no_color.snap b/tests/snapshots/term__same_line__medium_no_color.snap
new file mode 100644
index 0000000..6bb55ed
--- /dev/null
+++ b/tests/snapshots/term__same_line__medium_no_color.snap
@@ -0,0 +1,8 @@
+---
+source: codespan-reporting/tests/term.rs
+expression: TEST_DATA.emit_no_color(&config)
+---
+one_line.rs:3:12: error[E0499]: cannot borrow `v` as mutable more than once at a time
+error: aborting due to previous error
+ = For more information about this error, try `rustc --explain E0499`.
+
diff --git a/tests/snapshots/term__same_line__rich_color.snap b/tests/snapshots/term__same_line__rich_color.snap
index 59bd616..38addbb 100644
--- a/tests/snapshots/term__same_line__rich_color.snap
+++ b/tests/snapshots/term__same_line__rich_color.snap
@@ -3,7 +3,7 @@
 expression: TEST_DATA.emit_color(&config)
 ---
 {fg:Red bold bright}error[E0499]{bold bright}: cannot borrow `v` as mutable more than once at a time{/}
-  {fg:Blue}┌─{/} one_line.rs:3:5
+  {fg:Blue}┌─{/} one_line.rs:3:12
   {fg:Blue}│{/}
 {fg:Blue}3{/} {fg:Blue}│{/}     v.push({fg:Red}v{/}.pop().unwrap());
   {fg:Blue}│{/}     {fg:Blue}-{/} {fg:Blue}----{/} {fg:Red}^{/} {fg:Red}second mutable borrow occurs here{/}
diff --git a/tests/snapshots/term__same_line__rich_no_color.snap b/tests/snapshots/term__same_line__rich_no_color.snap
index 7342f3f..75511a4 100644
--- a/tests/snapshots/term__same_line__rich_no_color.snap
+++ b/tests/snapshots/term__same_line__rich_no_color.snap
@@ -3,7 +3,7 @@
 expression: TEST_DATA.emit_no_color(&config)
 ---
 error[E0499]: cannot borrow `v` as mutable more than once at a time
-  ┌─ one_line.rs:3:5
+  ┌─ one_line.rs:3:12

 3 │     v.push(v.pop().unwrap());
   │     - ---- ^ second mutable borrow occurs here
diff --git a/tests/snapshots/term__same_ranges__medium_color.snap b/tests/snapshots/term__same_ranges__medium_color.snap
new file mode 100644
index 0000000..095b975
--- /dev/null
+++ b/tests/snapshots/term__same_ranges__medium_color.snap
@@ -0,0 +1,7 @@
+---
+source: codespan-reporting/tests/term.rs
+expression: TEST_DATA.emit_color(&config)
+---
+same_range:1:5: {fg:Red bold bright}error{bold bright}: Unexpected token{/}
+
+
diff --git a/tests/snapshots/term__same_ranges__medium_no_color.snap b/tests/snapshots/term__same_ranges__medium_no_color.snap
new file mode 100644
index 0000000..a452022
--- /dev/null
+++ b/tests/snapshots/term__same_ranges__medium_no_color.snap
@@ -0,0 +1,7 @@
+---
+source: codespan-reporting/tests/term.rs
+expression: TEST_DATA.emit_no_color(&config)
+---
+same_range:1:5: error: Unexpected token
+
+
diff --git a/tests/snapshots/term__unicode__medium_no_color.snap b/tests/snapshots/term__unicode__medium_no_color.snap
new file mode 100644
index 0000000..b5f8780
--- /dev/null
+++ b/tests/snapshots/term__unicode__medium_no_color.snap
@@ -0,0 +1,29 @@
+---
+source: codespan-reporting/tests/term.rs
+expression: TEST_DATA.emit_no_color(&config)
+---
+unicode.rs:1:8: error[E0703]: invalid ABI: found `路濫狼á́́`
+ = valid ABIs:
+     - aapcs
+     - amdgpu-kernel
+     - C
+     - cdecl
+     - efiapi
+     - fastcall
+     - msp430-interrupt
+     - platform-intrinsic
+     - ptx-kernel
+     - Rust
+     - rust-call
+     - rust-intrinsic
+     - stdcall
+     - system
+     - sysv64
+     - thiscall
+     - unadjusted
+     - vectorcall
+     - win64
+     - x86-interrupt
+error: aborting due to previous error
+ = For more information about this error, try `rustc --explain E0703`.
+
diff --git a/tests/snapshots/term__unicode_spans__medium_no_color.snap b/tests/snapshots/term__unicode_spans__medium_no_color.snap
new file mode 100644
index 0000000..1b19451
--- /dev/null
+++ b/tests/snapshots/term__unicode_spans__medium_no_color.snap
@@ -0,0 +1,9 @@
+---
+source: codespan-reporting/tests/term.rs
+expression: TEST_DATA.emit_no_color(&config)
+---
+moon_jump.rs:1:1: error[E01]: cow may not jump during new moon.
+note: invalid unicode range
+note: invalid unicode range
+note: invalid unicode range
+
diff --git a/tests/term.rs b/tests/term.rs
index ee02372..15c9566 100644
--- a/tests/term.rs
+++ b/tests/term.rs
@@ -26,6 +26,17 @@
             insta::assert_snapshot!(TEST_DATA.emit_color(&config));
         }
     };
+    (medium_color) => {
+        #[test]
+        fn medium_color() {
+            let config = Config {
+                display_style: DisplayStyle::Medium,
+                ..TEST_CONFIG.clone()
+            };
+
+            insta::assert_snapshot!(TEST_DATA.emit_color(&config));
+        }
+    };
     (short_color) => {
         #[test]
         fn short_color() {
@@ -48,6 +59,17 @@
             insta::assert_snapshot!(TEST_DATA.emit_no_color(&config));
         }
     };
+    (medium_no_color) => {
+        #[test]
+        fn medium_no_color() {
+            let config = Config {
+                display_style: DisplayStyle::Medium,
+                ..TEST_CONFIG.clone()
+            };
+
+            insta::assert_snapshot!(TEST_DATA.emit_no_color(&config));
+        }
+    };
     (short_no_color) => {
         #[test]
         fn short_no_color() {
@@ -82,8 +104,10 @@
     }
 
     test_emit!(rich_color);
+    test_emit!(medium_color);
     test_emit!(short_color);
     test_emit!(rich_no_color);
+    test_emit!(medium_no_color);
     test_emit!(short_no_color);
 }
 
@@ -130,8 +154,10 @@
     }
 
     test_emit!(rich_color);
+    test_emit!(medium_color);
     test_emit!(short_color);
     test_emit!(rich_no_color);
+    test_emit!(medium_no_color);
     test_emit!(short_no_color);
 }
 
@@ -272,8 +298,10 @@
     }
 
     test_emit!(rich_color);
+    test_emit!(medium_color);
     test_emit!(short_color);
     test_emit!(rich_no_color);
+    test_emit!(medium_no_color);
     test_emit!(short_no_color);
 }
 
@@ -296,8 +324,10 @@
     }
 
     test_emit!(rich_color);
+    test_emit!(medium_color);
     test_emit!(short_color);
     test_emit!(rich_no_color);
+    test_emit!(medium_no_color);
     test_emit!(short_no_color);
 }
 
@@ -320,8 +350,36 @@
     }
 
     test_emit!(rich_color);
+    test_emit!(medium_color);
     test_emit!(short_color);
     test_emit!(rich_no_color);
+    test_emit!(medium_no_color);
+    test_emit!(short_no_color);
+}
+
+mod message_errorcode {
+    use super::*;
+
+    lazy_static::lazy_static! {
+        static ref TEST_DATA: TestData<'static, SimpleFiles<&'static str, &'static str>> = {
+            let files = SimpleFiles::new();
+
+            let diagnostics = vec![
+                Diagnostic::error().with_message("a message").with_code("E0001"),
+                Diagnostic::warning().with_message("a message").with_code("W001"),
+                Diagnostic::note().with_message("a message").with_code("N0815"),
+                Diagnostic::help().with_message("a message").with_code("H4711"),
+                Diagnostic::error().with_message("where did my errorcode go?").with_code(""),
+                Diagnostic::warning().with_message("where did my errorcode go?").with_code(""),
+                Diagnostic::note().with_message("where did my errorcode go?").with_code(""),
+                Diagnostic::help().with_message("where did my errorcode go?").with_code(""),
+            ];
+
+            TestData { files, diagnostics }
+        };
+    }
+
+    test_emit!(rich_no_color);
     test_emit!(short_no_color);
 }
 
@@ -353,8 +411,10 @@
     }
 
     test_emit!(rich_color);
+    test_emit!(medium_color);
     test_emit!(short_color);
     test_emit!(rich_no_color);
+    test_emit!(medium_no_color);
     test_emit!(short_no_color);
 }
 
@@ -379,8 +439,10 @@
     }
 
     test_emit!(rich_color);
+    test_emit!(medium_color);
     test_emit!(short_color);
     test_emit!(rich_no_color);
+    test_emit!(medium_no_color);
     test_emit!(short_no_color);
 }
 
@@ -463,8 +525,10 @@
     }
 
     test_emit!(rich_color);
+    test_emit!(medium_color);
     test_emit!(short_color);
     test_emit!(rich_no_color);
+    test_emit!(medium_no_color);
     test_emit!(short_no_color);
 }
 
@@ -540,8 +604,10 @@
     }
 
     test_emit!(rich_color);
+    test_emit!(medium_color);
     test_emit!(short_color);
     test_emit!(rich_no_color);
+    test_emit!(medium_no_color);
     test_emit!(short_no_color);
 }
 
@@ -569,9 +635,10 @@
                     .with_message("match arms have incompatible types")
                     .with_code("E0308")
                     .with_labels(vec![
+                        // this secondary label is before the primary label to test the locus calculation (see issue #259)
+                        Label::secondary((), 89..134).with_message("this is found to be of type `Result<ByteIndex, LineIndexOutOfBoundsError>`"),
                         Label::primary((), 230..351).with_message("expected enum `Result`, found struct `LineIndexOutOfBoundsError`"),
                         Label::secondary((), 8..362).with_message("`match` arms have incompatible types"),
-                        Label::secondary((), 89..134).with_message("this is found to be of type `Result<ByteIndex, LineIndexOutOfBoundsError>`"),
                         Label::secondary((), 167..195).with_message("this is found to be of type `Result<ByteIndex, LineIndexOutOfBoundsError>`"),
                     ])
                     .with_notes(vec![unindent::unindent(
@@ -587,8 +654,10 @@
     }
 
     test_emit!(rich_color);
+    test_emit!(medium_color);
     test_emit!(short_color);
     test_emit!(rich_no_color);
+    test_emit!(medium_no_color);
     test_emit!(short_no_color);
 }
 
@@ -792,6 +861,7 @@
     }
 
     test_emit!(rich_no_color);
+    test_emit!(medium_no_color);
     test_emit!(short_no_color);
 }
 
@@ -842,5 +912,118 @@
     }
 
     test_emit!(rich_no_color);
+    test_emit!(medium_no_color);
     test_emit!(short_no_color);
 }
+
+mod position_indicator {
+    use super::*;
+
+    lazy_static::lazy_static! {
+        static ref TEST_DATA: TestData<'static, SimpleFile<&'static str, String>> = {
+            let file = SimpleFile::new(
+                "tests/main.js",
+                [
+                    "\"use strict\";",
+                    "let zero=0;",
+                    "function foo() {",
+                    "  \"use strict\";",
+                    "  one=1;",
+                    "}",
+                ].join("\n"),
+            );
+            let diagnostics = vec![
+                Diagnostic::warning()
+                    .with_code("ParserWarning")
+                    .with_message("The strict mode declaration in the body of function `foo` is redundant, as the outer scope is already in strict mode")
+                    .with_labels(vec![
+                        Label::primary((), 45..57)
+                            .with_message("This strict mode declaration is redundant"),
+                        Label::secondary((), 0..12)
+                            .with_message("Strict mode is first declared here"),
+                    ]),
+            ];
+            TestData{files: file, diagnostics }
+        };
+    }
+
+    test_emit!(rich_no_color);
+    test_emit!(medium_no_color);
+    test_emit!(short_no_color);
+}
+
+mod multiline_omit {
+    use super::*;
+
+    lazy_static::lazy_static! {
+        static ref TEST_CONFIG: Config = Config {
+            styles: Styles::with_blue(Color::Blue),
+            start_context_lines: 2,
+            end_context_lines: 1,
+            ..Config::default()
+        };
+
+        static ref TEST_DATA: TestData<'static, SimpleFiles<&'static str, String>> = {
+            let mut files = SimpleFiles::new();
+
+            let file_id1 = files.add(
+                "empty_if_comments.lua",
+                [
+                    "elseif 3 then", // primary label starts here
+                    "",              // context line
+                    "",
+                    "",
+                    "",
+                    "",
+                    "",
+                    "",
+                    "",     // context line
+                    "else", // primary label ends here
+                ]
+                .join("\n"),
+            );
+
+            let file_id2 = files.add(
+                "src/lib.rs",
+                [
+                    "fn main() {",
+                    "    1",   // primary label starts here
+                    "    + 1", // context line
+                    "    + 1", // skip
+                    "    + 1", // skip
+                    "    + 1", // skip
+                    "    +1",  // secondary label here
+                    "    + 1", // this single line will not be skipped; the previously filtered out label must be retrieved
+                    "    + 1", // context line
+                    "    + 1", // primary label ends here
+                    "}",
+                ]
+                .join("\n"),
+            );
+
+            let diagnostics = vec![
+                Diagnostic::error()
+                    .with_message("empty elseif block")
+                    .with_code("empty_if")
+                    .with_labels(vec![
+                        Label::primary(file_id1, 0..23),
+                        Label::secondary(file_id1, 15..21).with_message("content should be in here"),
+                    ]),
+                Diagnostic::error()
+                    .with_message("mismatched types")
+                    .with_code("E0308")
+                    .with_labels(vec![
+                        Label::primary(file_id2, 17..80).with_message("expected (), found integer"),
+                        Label::secondary(file_id2, 55..55).with_message("missing whitespace"),
+                    ])
+                    .with_notes(vec![
+                        "note:\texpected type `()`\n\tfound type `{integer}`".to_owned()
+                    ]),
+            ];
+
+            TestData { files, diagnostics }
+        };
+    }
+
+    test_emit!(rich_no_color);
+}