1use alloc::vec::Vec;
10
11#[derive(Clone)]
12pub struct LineIndex {
13 line_offsets: Vec<usize>,
15}
16
17impl LineIndex {
18 pub fn new(text: &str) -> LineIndex {
19 let mut line_offsets: Vec<usize> = alloc::vec![0];
20
21 let mut offset = 0;
22
23 for c in text.chars() {
24 offset += c.len_utf8();
25 if c == '\n' {
26 line_offsets.push(offset);
27 }
28 }
29
30 LineIndex { line_offsets }
31 }
32
33 pub fn line_col(&self, input: &str, pos: usize) -> (usize, usize) {
37 let line = self.line_offsets.partition_point(|&it| it <= pos) - 1;
38 let first_offset = self.line_offsets[line];
39
40 let line_str = &input[first_offset..pos];
42 let col = line_str.chars().count();
43
44 (line + 1, col + 1)
45 }
46}
47
48#[cfg(test)]
49mod tests {
50 use super::*;
51
52 #[allow(clippy::zero_prefixed_literal)]
53 #[test]
54 fn test_line_index() {
55 let text = "hello 你好 A🎈C\nworld";
56 let table = [
57 (00, 1, 1, 'h'),
58 (01, 1, 2, 'e'),
59 (02, 1, 3, 'l'),
60 (03, 1, 4, 'l'),
61 (04, 1, 5, 'o'),
62 (05, 1, 6, ' '),
63 (06, 1, 7, '你'),
64 (09, 1, 8, '好'),
65 (12, 1, 9, ' '),
66 (13, 1, 10, 'A'),
67 (14, 1, 11, '🎈'),
68 (18, 1, 12, 'C'),
69 (19, 1, 13, '\n'),
70 (20, 2, 1, 'w'),
71 (21, 2, 2, 'o'),
72 (22, 2, 3, 'r'),
73 (23, 2, 4, 'l'),
74 (24, 2, 5, 'd'),
75 ];
76
77 let index = LineIndex::new(text);
78 for &(offset, line, col, c) in table.iter() {
79 let res = index.line_col(text, offset);
80 assert_eq!(
81 (res.0, res.1),
82 (line, col),
83 "Expected: ({}, {}, {}, {:?})",
84 offset,
85 line,
86 col,
87 c
88 );
89 }
90 }
91}