Skip to content

fix!: Take in byte spans #90

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Mar 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 12 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ maintenance = { status = "actively-developed" }

[dependencies]
anstyle = "1.0.4"
itertools = "0.12.1"
unicode-width = "0.1.11"

[dev-dependencies]
Expand Down
4 changes: 2 additions & 2 deletions benches/simple.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,12 @@ fn create_snippet(renderer: Renderer) {
SourceAnnotation {
label: "expected `Option<String>` because of return type",
annotation_type: AnnotationType::Warning,
range: (5, 19),
range: 5..19,
},
SourceAnnotation {
label: "expected enum `std::option::Option`",
annotation_type: AnnotationType::Error,
range: (26, 724),
range: 26..724,
},
],
}],
Expand Down
4 changes: 2 additions & 2 deletions examples/expected_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,12 @@ fn main() {
SourceAnnotation {
label: "",
annotation_type: AnnotationType::Error,
range: (193, 195),
range: 193..195,
},
SourceAnnotation {
label: "while parsing this struct",
annotation_type: AnnotationType::Info,
range: (34, 50),
range: 34..50,
},
],
}],
Expand Down
2 changes: 1 addition & 1 deletion examples/footer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ fn main() {
fold: false,
annotations: vec![SourceAnnotation {
label: "expected struct `annotate_snippets::snippet::Slice`, found reference",
range: (21, 24),
range: 21..24,
annotation_type: AnnotationType::Error,
}],
}],
Expand Down
4 changes: 2 additions & 2 deletions examples/format.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,12 @@ fn main() {
SourceAnnotation {
label: "expected `Option<String>` because of return type",
annotation_type: AnnotationType::Warning,
range: (5, 19),
range: 5..19,
},
SourceAnnotation {
label: "expected enum `std::option::Option`",
annotation_type: AnnotationType::Error,
range: (26, 724),
range: 26..724,
},
],
}],
Expand Down
87 changes: 43 additions & 44 deletions src/renderer/display_list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,10 @@
//!
//! The above snippet has been built out of the following structure:
use crate::snippet;
use itertools::FoldWhile::{Continue, Done};
use itertools::Itertools;
use std::fmt::{Display, Write};
use std::ops::Range;
use std::{cmp, fmt};

use crate::renderer::{stylesheet::Stylesheet, Margin, Style};
Expand Down Expand Up @@ -766,7 +769,7 @@ fn format_slice(
has_footer: bool,
margin: Option<Margin>,
) -> Vec<DisplayLine<'_>> {
let main_range = slice.annotations.first().map(|x| x.range.0);
let main_range = slice.annotations.first().map(|x| x.range.start);
let origin = slice.origin;
let need_empty_header = origin.is_some() || is_first;
let mut body = format_body(slice, need_empty_header, has_footer, margin);
Expand Down Expand Up @@ -804,13 +807,27 @@ fn format_header<'a>(

for item in body {
if let DisplayLine::Source {
line: DisplaySourceLine::Content { range, .. },
line: DisplaySourceLine::Content { text, range },
lineno,
..
} = item
{
if main_range >= range.0 && main_range <= range.1 {
col = main_range - range.0 + 1;
let char_column = text
.chars()
.map(|c| unicode_width::UnicodeWidthChar::width(c).unwrap_or(0))
.chain(std::iter::once(1)) // treat the end of line as single-width
.enumerate()
.fold_while((0, 0), |(count, acc), (i, width)| {
if acc <= main_range - range.0 {
Continue((i, acc + width))
} else {
Done((count, acc))
}
})
.into_inner()
.0;
col = char_column + 1;
line_offset = lineno.unwrap_or(1);
break;
}
Expand Down Expand Up @@ -932,11 +949,11 @@ fn format_body(
has_footer: bool,
margin: Option<Margin>,
) -> Vec<DisplayLine<'_>> {
let source_len = slice.source.chars().count();
let source_len = slice.source.len();
if let Some(bigger) = slice.annotations.iter().find_map(|x| {
// Allow highlighting one past the last character in the source.
if source_len + 1 < x.range.1 {
Some(x.range)
if source_len + 1 < x.range.end {
Some(&x.range)
} else {
None
}
Expand All @@ -955,18 +972,14 @@ fn format_body(
struct LineInfo {
line_start_index: usize,
line_end_index: usize,
// How many spaces each character in the line take up when displayed
char_widths: Vec<usize>,
}

for (line, end_line) in CursorLines::new(slice.source) {
let line_length = line.chars().count();
let line_range = (current_index, current_index + line_length);
let char_widths = line
let line_length: usize = line
.chars()
.map(|c| unicode_width::UnicodeWidthChar::width(c).unwrap_or(0))
.chain(std::iter::once(1)) // treat the end of line as single-width
.collect::<Vec<_>>();
.sum();
let line_range = (current_index, current_index + line_length);
body.push(DisplayLine::Source {
lineno: Some(current_line),
inline_marks: vec![],
Expand All @@ -978,7 +991,6 @@ fn format_body(
line_info.push(LineInfo {
line_start_index: line_range.0,
line_end_index: line_range.1,
char_widths,
});
current_line += 1;
current_index += line_length + end_line as usize;
Expand All @@ -991,7 +1003,6 @@ fn format_body(
LineInfo {
line_start_index,
line_end_index,
char_widths,
},
) in line_info.into_iter().enumerate()
{
Expand All @@ -1007,21 +1018,13 @@ fn format_body(
_ => DisplayAnnotationType::from(annotation.annotation_type),
};
match annotation.range {
(start, _) if start > line_end_index => true,
(start, end)
Range { start, .. } if start > line_end_index => true,
Range { start, end }
if start >= line_start_index && end <= line_end_index
|| start == line_end_index && end - start <= 1 =>
{
let annotation_start_col = char_widths
.iter()
.take(start - line_start_index)
.sum::<usize>()
- margin_left;
let annotation_end_col = char_widths
.iter()
.take(end - line_start_index)
.sum::<usize>()
- margin_left;
let annotation_start_col = start - line_start_index - margin_left;
let annotation_end_col = end - line_start_index - margin_left;
let range = (annotation_start_col, annotation_end_col);
body.insert(
body_idx + 1,
Expand All @@ -1045,7 +1048,7 @@ fn format_body(
annotation_line_count += 1;
false
}
(start, end)
Range { start, end }
if start >= line_start_index
&& start <= line_end_index
&& end > line_end_index =>
Expand All @@ -1064,10 +1067,7 @@ fn format_body(
});
}
} else {
let annotation_start_col = char_widths
.iter()
.take(start - line_start_index)
.sum::<usize>();
let annotation_start_col = start - line_start_index;
let range = (annotation_start_col, annotation_start_col + 1);
body.insert(
body_idx + 1,
Expand All @@ -1092,7 +1092,7 @@ fn format_body(
}
true
}
(start, end) if start < line_start_index && end > line_end_index => {
Range { start, end } if start < line_start_index && end > line_end_index => {
if let DisplayLine::Source {
ref mut inline_marks,
..
Expand All @@ -1107,7 +1107,7 @@ fn format_body(
}
true
}
(start, end)
Range { start, end }
if start < line_start_index
&& end >= line_start_index
&& end <= line_end_index =>
Expand All @@ -1125,11 +1125,7 @@ fn format_body(
});
}

let end_mark = char_widths
.iter()
.take(end - line_start_index)
.sum::<usize>()
.saturating_sub(1);
let end_mark = (end - line_start_index).saturating_sub(1);
let range = (end_mark - margin_left, (end_mark + 1) - margin_left);
body.insert(
body_idx + 1,
Expand Down Expand Up @@ -1380,7 +1376,7 @@ mod tests {
let line_2 = "This is line 2";
let source = [line_1, line_2].join("\n");
// In line 2
let range = (22, 24);
let range = 22..24;
let input = snippet::Snippet {
title: None,
footer: vec![],
Expand All @@ -1389,7 +1385,7 @@ mod tests {
line_start: 5402,
origin: None,
annotations: vec![snippet::SourceAnnotation {
range,
range: range.clone(),
label: "Test annotation",
annotation_type: snippet::AnnotationType::Info,
}],
Expand Down Expand Up @@ -1430,7 +1426,10 @@ mod tests {
style: DisplayTextStyle::Regular,
}],
},
range: (range.0 - (line_1.len() + 1), range.1 - (line_1.len() + 1)),
range: (
range.start - (line_1.len() + 1),
range.end - (line_1.len() + 1),
),
annotation_type: DisplayAnnotationType::Info,
annotation_part: DisplayAnnotationPart::Standalone,
},
Expand Down Expand Up @@ -1480,7 +1479,7 @@ mod tests {
footer: vec![],
slices: vec![snippet::Slice {
annotations: vec![snippet::SourceAnnotation {
range: (0, source.len() + 2),
range: 0..source.len() + 2,
label,
annotation_type: snippet::AnnotationType::Error,
}],
Expand All @@ -1507,7 +1506,7 @@ mod tests {
line_start: 1,
origin: Some("<current file>"),
annotations: vec![snippet::SourceAnnotation {
range: (19, 23),
range: 19..23,
label: "oops",
annotation_type: snippet::AnnotationType::Error,
}],
Expand Down
5 changes: 4 additions & 1 deletion src/snippet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@
//! };
//! ```

use std::ops::Range;

/// Primary structure provided for formatting
#[derive(Debug, Default)]
pub struct Snippet<'a> {
Expand Down Expand Up @@ -70,7 +72,8 @@ pub enum AnnotationType {
/// An annotation for a `Slice`.
#[derive(Debug)]
pub struct SourceAnnotation<'a> {
pub range: (usize, usize),
/// The byte range of the annotation in the `source` string
pub range: Range<usize>,
pub label: &'a str,
pub annotation_type: AnnotationType,
}
Expand Down
3 changes: 2 additions & 1 deletion tests/fixtures/deserialize.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use serde::{Deserialize, Deserializer, Serialize};
use std::ops::Range;

use annotate_snippets::{
renderer::Margin, Annotation, AnnotationType, Renderer, Slice, Snippet, SourceAnnotation,
Expand Down Expand Up @@ -122,7 +123,7 @@ where
#[derive(Serialize, Deserialize)]
#[serde(remote = "SourceAnnotation")]
pub struct SourceAnnotationDef<'a> {
pub range: (usize, usize),
pub range: Range<usize>,
#[serde(borrow)]
pub label: &'a str,
#[serde(with = "AnnotationTypeDef")]
Expand Down
Loading