Skip to main content

tanzim_parse/
span.rs

1//! Shared span-to-Location conversion helpers used by format modules.
2
3/// Whether `bytes` contain no newline (single-line document).
4pub fn is_single_line(bytes: &[u8]) -> bool {
5    for &byte in bytes {
6        if byte == b'\n' {
7            return false;
8        }
9    }
10    true
11}
12
13/// 1-based line and UTF-8 character column at `byte_offset` in `text`.
14pub fn line_column(text: &str, byte_offset: usize) -> (usize, usize) {
15    let mut line = 1usize;
16    let mut column = 1usize;
17    let mut index = 0usize;
18    while index < byte_offset && index < text.len() {
19        let ch = text[index..].chars().next().expect("valid utf-8");
20        let ch_len = ch.len_utf8();
21        if ch == '\n' {
22            line += 1;
23            column = 1;
24        } else {
25            column += 1;
26        }
27        index += ch_len;
28    }
29    (line, column)
30}
31
32/// UTF-8 character count in `text[start_byte..end_byte]`.
33pub fn char_count(text: &str, start_byte: usize, end_byte: usize) -> usize {
34    let end_byte = end_byte.min(text.len());
35    if start_byte >= end_byte {
36        return 0;
37    }
38    let mut count = 0usize;
39    let mut index = start_byte;
40    while index < end_byte {
41        let ch = text[index..].chars().next().expect("valid utf-8");
42        count += 1;
43        index += ch.len_utf8();
44    }
45    count
46}
47
48/// 1-based line and UTF-8 character column at `byte_offset` in `text`.
49pub fn line_column_from_line(text: &str, line_number: usize, byte_offset: usize) -> usize {
50    let mut line = 1usize;
51    let mut column = 1usize;
52    let mut index = 0usize;
53    while index < text.len() {
54        if line == line_number && index >= byte_offset {
55            return column;
56        }
57        let ch = text[index..].chars().next().expect("valid utf-8");
58        let ch_len = ch.len_utf8();
59        if ch == '\n' {
60            line += 1;
61            column = 1;
62        } else {
63            column += 1;
64        }
65        index += ch_len;
66    }
67    if line == line_number { column } else { 1 }
68}
69
70#[cfg(test)]
71mod tests {
72    use super::*;
73
74    #[test]
75    fn line_column_counts_utf8_chars() {
76        let text = "α: 1\nb: 2";
77        let (line, column) = line_column(text, 0);
78        assert_eq!(line, 1);
79        assert_eq!(column, 1);
80        let (line, column) = line_column(text, text.find('\n').unwrap() + 1);
81        assert_eq!(line, 2);
82        assert_eq!(column, 1);
83    }
84}