source_map/
lines_columns_indexes.rs1#[derive(Clone, Debug)]
3pub struct LineStarts(pub(crate) Vec<usize>);
4
5impl LineStarts {
6 pub fn new(source: &str) -> LineStarts {
8 Self(
9 std::iter::once(0)
10 .chain(source.match_indices('\n').map(|(i, _)| i + 1))
11 .collect(),
12 )
13 }
14
15 pub fn append(&mut self, start: usize, appended: &str) {
16 self.0
17 .extend(appended.match_indices('\n').map(|(i, _)| i + 1 + start))
18 }
19
20 pub fn byte_indexes_on_same_line(&self, pos1: usize, pos2: usize) -> bool {
21 debug_assert!(pos1 <= pos2);
22 self.0
23 .windows(2)
24 .find_map(|w| {
25 let range = w[0]..=w[1];
26 range.contains(&pos1).then_some(range)
27 })
28 .expect("could not find splits for pos1")
29 .contains(&pos2)
30 }
31
32 pub fn byte_indexes_crosses_lines(&self, pos1: usize, pos2: usize) -> usize {
33 debug_assert!(pos1 <= pos2);
34 let first_line_backwards = self.get_line_pos_is_on(pos1);
35 let second_line_backwards = self.get_line_pos_is_on(pos2);
36 second_line_backwards - first_line_backwards
37 }
38
39 pub fn byte_indexes_on_different_lines(&self, pos1: usize, pos2: usize) -> bool {
40 self.byte_indexes_crosses_lines(pos1, pos2) > 0
41 }
42
43 pub(crate) fn get_line_pos_is_on(&self, pos: usize) -> usize {
45 self.get_line_and_column_pos_is_on(pos).0
46 }
47
48 pub(crate) fn get_line_and_column_pos_is_on(&self, pos: usize) -> (usize, usize) {
50 let backwards_index = self
51 .0
52 .iter()
53 .rev()
54 .position(|index| pos >= *index)
55 .expect("pos out of bounds");
56
57 let line = (self.0.len() - 1) - backwards_index;
58 (line, pos - self.0[line])
59 }
60}
61
62#[cfg(test)]
63mod tests {
64 use super::LineStarts;
65
66 fn get_source() -> String {
67 std::fs::read_to_string("README.md").expect("No README")
68 }
69
70 #[test]
71 fn split_lines() {
72 let source = get_source();
73
74 let line_starts = LineStarts::new(&source);
75 let expected_lines = source.lines().collect::<Vec<_>>();
76 let mut actual_lines = Vec::new();
77
78 let mut iterator = line_starts.0.into_iter();
79 let mut last = iterator.next().unwrap();
80 for part in iterator {
81 let value = &source[last..part];
82 let value = value.strip_suffix('\n').unwrap();
83 let value = value.strip_suffix('\r').unwrap_or(value);
84 actual_lines.push(value);
85 last = part;
86 }
87
88 assert_eq!(expected_lines, actual_lines);
89 }
90
91 #[test]
92 fn append() {
93 let source = get_source();
94
95 let whole = LineStarts::new(&source);
96 let at = 100;
97 let (left, right) = source.split_at(at);
98
99 let mut left = LineStarts::new(left);
100 left.append(at, right);
101
102 assert_eq!(whole.0, left.0);
103 }
104
105 #[test]
106 fn byte_indexes_crosses_lines() {
107 let source = get_source();
108
109 let line_starts = LineStarts::new(&source);
110
111 let start = 100;
112 let end = 200;
113 let lines_in_between = source[start..end].chars().filter(|c| *c == '\n').count();
114
115 assert_eq!(
116 line_starts.byte_indexes_crosses_lines(start, end),
117 lines_in_between
118 );
119 }
120
121 #[test]
122 fn byte_indexes_on_same_line() {
123 let source = get_source();
124
125 let line_starts = LineStarts::new(&source);
126
127 let start = 100;
128 let end = start
129 + source[start..]
130 .chars()
131 .take_while(|c| *c == '\n')
132 .map(|c| c.len_utf16())
133 .sum::<usize>();
134
135 assert!(line_starts.byte_indexes_on_same_line(start, end));
136 }
137}