Repository: rust-lang/annotate-snippets-rs (rust)

Issue:
Off-by-one error in multiline error highlights
In both cases, the highlight begins at letter `e` and ends at letter `l`. In the one-line case, it is correct:
```
error: oops
 --> <current file>:1:6
  |
1 | abcd efgh ijkl mnop
  |      ^^^^^^^^^ oops
  |
```
But in multiple lines it is off by one - the end pointer points to one past the end rather than the last character of the highlight:
```
error: oops
 --> <current file>:1:6
  |
1 |   abcd efgh
  |  ______^
2 | | ijkl mnop
  | |_____^ oops
  |
```
I could propose code but I'm not sure I'll get all the details of continuations right since I haven't used those and am not certain of the desired behavior. Here's the issue presented as a test case.
```rust
use annotate_snippets::{display_list::DisplayList, snippet};
use snippet::Snippet;

#[test]
fn with_space_works() {
    let snippets = Snippet {
        title: Some(snippet::Annotation {
            id: None,
            label: Some("oops"),
            annotation_type: snippet::AnnotationType::Error,
        }),
        footer: vec![],
        slices: vec![snippet::Slice {
            source: "abcd efgh ijkl mnop",
            line_start: 1,
            origin: Some("<current file>"),
            annotations: vec![snippet::SourceAnnotation {
                range: (5, 14),
                label: "oops",
                annotation_type: snippet::AnnotationType::Error,
            }],
            fold: true,
        }],
        opt: Default::default(),
    };
    let expected = r#"error: oops
 --> <current file>:1:6
  |
1 | abcd efgh ijkl mnop
  |      ^^^^^^^^^ oops
  |"#;
    assert_eq!(DisplayList::from(snippets).to_string(), expected);
}

#[test]
fn with_newline_fails() {
    let snippets = Snippet {
        title: Some(snippet::Annotation {
            id: None,
            label: Some("oops"),
            annotation_type: snippet::AnnotationType::Error,
        }),
        footer: vec![],
        slices: vec![snippet::Slice {
            source: "abcd efgh\nijkl mnop",
            line_start: 1,
            origin: Some("<current file>"),
            annotations: vec![snippet::SourceAnnotation {
                range: (5, 14),
                label: "oops",
                annotation_type: snippet::AnnotationType::Error,
            }],
            fold: true,
        }],
        opt: Default::default(),
    };
    let expected = r#"error: oops
 --> <current file>:1:6
  |
1 |   abcd efgh
  |  ______^
2 | | ijkl mnop
  | |____^ oops
  |"#;

    assert_eq!(DisplayList::from(snippets).to_string(), expected);
}
```

Pull request:
fix off by one error in multiline highlighting
Implements the fix described in #41.

Fixes #41

Patch:
diff --git a/src/display_list/from_snippet.rs b/src/display_list/from_snippet.rs
index d32d1e0..272c063 100644
--- a/src/display_list/from_snippet.rs
+++ b/src/display_list/from_snippet.rs
@@ -427,9 +427,10 @@ fn format_body(
                             });
                         }
 
+                        let end_mark = (end - line_start).saturating_sub(1);
                         let range = (
-                            (end - line_start) - margin_left,
-                            (end - line_start + 1) - margin_left,
+                            end_mark - margin_left,
+                            (end_mark + 1) - margin_left,
                         );
                         body.insert(
                             body_idx + 1,


Test patch:
diff --git a/tests/fixtures/no-color/multiline_annotation.toml b/tests/fixtures/no-color/multiline_annotation.toml
index bdb577f..c3dc1e9 100644
--- a/tests/fixtures/no-color/multiline_annotation.toml
+++ b/tests/fixtures/no-color/multiline_annotation.toml
@@ -33,7 +33,7 @@ range = [5, 19]
 [[slices.annotations]]
 label = "expected enum `std::option::Option`, found ()"
 annotation_type = "Error"
-range = [22, 765]
+range = [22, 766]
 [title]
 label = "mismatched types"
 id = "E0308"
diff --git a/tests/fixtures/no-color/multiline_annotation2.toml b/tests/fixtures/no-color/multiline_annotation2.toml
index 6ec0b1f..845bf9f 100644
--- a/tests/fixtures/no-color/multiline_annotation2.toml
+++ b/tests/fixtures/no-color/multiline_annotation2.toml
@@ -10,7 +10,7 @@ fold = false
 [[slices.annotations]]
 label = "missing fields `lineno`, `content`"
 annotation_type = "Error"
-range = [31, 127]
+range = [31, 128]
 
 [title]
 label = "pattern does not mention fields `lineno`, `content`"