quarto_source_map/
utils.rs1use crate::types::{Location, Range};
4
5pub fn offset_to_location(source: &str, offset: usize) -> Option<Location> {
9 if offset > source.len() {
10 return None;
11 }
12
13 let mut row = 0;
14 let mut column = 0;
15 let mut current_offset = 0;
16
17 for ch in source.chars() {
18 if current_offset >= offset {
19 break;
20 }
21
22 if ch == '\n' {
23 row += 1;
24 column = 0;
25 } else {
26 column += 1;
27 }
28
29 current_offset += ch.len_utf8();
30 }
31
32 Some(Location {
33 offset,
34 row,
35 column,
36 })
37}
38
39pub fn line_col_to_offset(source: &str, line: usize, col: usize) -> Option<usize> {
43 let mut current_line = 0;
44 let mut current_col = 0;
45 let mut offset = 0;
46
47 for ch in source.chars() {
48 if current_line == line && current_col == col {
49 return Some(offset);
50 }
51
52 if ch == '\n' {
53 current_line += 1;
54 current_col = 0;
55 } else {
56 current_col += 1;
57 }
58
59 offset += ch.len_utf8();
60 }
61
62 if current_line == line && current_col == col {
64 return Some(offset);
65 }
66
67 None
68}
69
70pub fn range_from_offsets(start: usize, end: usize) -> Range {
76 Range {
77 start: Location {
78 offset: start,
79 row: 0,
80 column: 0,
81 },
82 end: Location {
83 offset: end,
84 row: 0,
85 column: 0,
86 },
87 }
88}
89
90#[cfg(test)]
91mod tests {
92 use super::*;
93
94 #[test]
95 fn test_offset_to_location_simple() {
96 let source = "hello\nworld";
97
98 let loc = offset_to_location(source, 0).unwrap();
100 assert_eq!(loc.offset, 0);
101 assert_eq!(loc.row, 0);
102 assert_eq!(loc.column, 0);
103
104 let loc = offset_to_location(source, 3).unwrap();
106 assert_eq!(loc.offset, 3);
107 assert_eq!(loc.row, 0);
108 assert_eq!(loc.column, 3);
109
110 let loc = offset_to_location(source, 6).unwrap();
112 assert_eq!(loc.offset, 6);
113 assert_eq!(loc.row, 1);
114 assert_eq!(loc.column, 0);
115
116 let loc = offset_to_location(source, 9).unwrap();
118 assert_eq!(loc.offset, 9);
119 assert_eq!(loc.row, 1);
120 assert_eq!(loc.column, 3);
121 }
122
123 #[test]
124 fn test_offset_to_location_out_of_bounds() {
125 let source = "hello";
126 assert!(offset_to_location(source, 100).is_none());
127 }
128
129 #[test]
130 fn test_offset_to_location_end() {
131 let source = "hello";
132 let loc = offset_to_location(source, 5).unwrap();
133 assert_eq!(loc.offset, 5);
134 assert_eq!(loc.row, 0);
135 assert_eq!(loc.column, 5);
136 }
137
138 #[test]
139 fn test_line_col_to_offset_simple() {
140 let source = "hello\nworld";
141
142 let offset = line_col_to_offset(source, 0, 0).unwrap();
144 assert_eq!(offset, 0);
145
146 let offset = line_col_to_offset(source, 0, 3).unwrap();
148 assert_eq!(offset, 3);
149
150 let offset = line_col_to_offset(source, 1, 0).unwrap();
152 assert_eq!(offset, 6);
153
154 let offset = line_col_to_offset(source, 1, 3).unwrap();
156 assert_eq!(offset, 9);
157 }
158
159 #[test]
160 fn test_line_col_to_offset_out_of_bounds() {
161 let source = "hello\nworld";
162 assert!(line_col_to_offset(source, 10, 0).is_none());
163 assert!(line_col_to_offset(source, 0, 100).is_none());
164 }
165
166 #[test]
167 fn test_line_col_to_offset_end() {
168 let source = "hello";
169 let offset = line_col_to_offset(source, 0, 5).unwrap();
170 assert_eq!(offset, 5);
171 }
172
173 #[test]
174 fn test_roundtrip() {
175 let source = "hello\nworld\ntest";
176
177 for test_offset in [0, 3, 6, 10, 16] {
179 let loc = offset_to_location(source, test_offset).unwrap();
180 let back_to_offset = line_col_to_offset(source, loc.row, loc.column).unwrap();
181 assert_eq!(test_offset, back_to_offset);
182 }
183 }
184
185 #[test]
186 fn test_range_from_offsets() {
187 let range = range_from_offsets(10, 20);
188 assert_eq!(range.start.offset, 10);
189 assert_eq!(range.end.offset, 20);
190 assert_eq!(range.start.row, 0);
191 assert_eq!(range.start.column, 0);
192 }
193
194 #[test]
195 fn test_offset_to_location_multiline() {
196 let source = "line1\nline2\nline3";
197
198 let loc = offset_to_location(source, 0).unwrap();
200 assert_eq!(loc.row, 0);
201 assert_eq!(loc.column, 0);
202
203 let loc = offset_to_location(source, 6).unwrap();
204 assert_eq!(loc.row, 1);
205 assert_eq!(loc.column, 0);
206
207 let loc = offset_to_location(source, 12).unwrap();
208 assert_eq!(loc.row, 2);
209 assert_eq!(loc.column, 0);
210 }
211}