aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_editor/src/line_index.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_editor/src/line_index.rs')
-rw-r--r--crates/ra_editor/src/line_index.rs283
1 files changed, 179 insertions, 104 deletions
diff --git a/crates/ra_editor/src/line_index.rs b/crates/ra_editor/src/line_index.rs
index aab7e4081..898fee7e0 100644
--- a/crates/ra_editor/src/line_index.rs
+++ b/crates/ra_editor/src/line_index.rs
@@ -4,8 +4,8 @@ use superslice::Ext;
4 4
5#[derive(Clone, Debug, PartialEq, Eq)] 5#[derive(Clone, Debug, PartialEq, Eq)]
6pub struct LineIndex { 6pub struct LineIndex {
7 newlines: Vec<TextUnit>, 7 pub(crate) newlines: Vec<TextUnit>,
8 utf16_lines: FxHashMap<u32, Vec<Utf16Char>>, 8 pub(crate) utf16_lines: FxHashMap<u32, Vec<Utf16Char>>,
9} 9}
10 10
11#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] 11#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
@@ -15,9 +15,9 @@ pub struct LineCol {
15} 15}
16 16
17#[derive(Clone, Debug, Hash, PartialEq, Eq)] 17#[derive(Clone, Debug, Hash, PartialEq, Eq)]
18struct Utf16Char { 18pub(crate) struct Utf16Char {
19 start: TextUnit, 19 pub(crate) start: TextUnit,
20 end: TextUnit, 20 pub(crate) end: TextUnit,
21} 21}
22 22
23impl Utf16Char { 23impl Utf16Char {
@@ -62,6 +62,12 @@ impl LineIndex {
62 62
63 curr_col += char_len; 63 curr_col += char_len;
64 } 64 }
65
66 // Save any utf-16 characters seen in the last line
67 if utf16_chars.len() > 0 {
68 utf16_lines.insert(line, utf16_chars);
69 }
70
65 LineIndex { 71 LineIndex {
66 newlines, 72 newlines,
67 utf16_lines, 73 utf16_lines,
@@ -122,111 +128,179 @@ impl LineIndex {
122 } 128 }
123} 129}
124 130
125#[test] 131#[cfg(test)]
126fn test_line_index() { 132/// Simple reference implementation to use in proptests
127 let text = "hello\nworld"; 133pub fn to_line_col(text: &str, offset: TextUnit) -> LineCol {
128 let index = LineIndex::new(text); 134 let mut res = LineCol {
129 assert_eq!( 135 line: 0,
130 index.line_col(0.into()), 136 col_utf16: 0,
131 LineCol { 137 };
132 line: 0, 138 for (i, c) in text.char_indices() {
133 col_utf16: 0 139 if i + c.len_utf8() > offset.to_usize() {
134 } 140 // if it's an invalid offset, inside a multibyte char
135 ); 141 // return as if it was at the start of the char
136 assert_eq!( 142 break;
137 index.line_col(1.into()),
138 LineCol {
139 line: 0,
140 col_utf16: 1
141 }
142 );
143 assert_eq!(
144 index.line_col(5.into()),
145 LineCol {
146 line: 0,
147 col_utf16: 5
148 }
149 );
150 assert_eq!(
151 index.line_col(6.into()),
152 LineCol {
153 line: 1,
154 col_utf16: 0
155 }
156 );
157 assert_eq!(
158 index.line_col(7.into()),
159 LineCol {
160 line: 1,
161 col_utf16: 1
162 }
163 );
164 assert_eq!(
165 index.line_col(8.into()),
166 LineCol {
167 line: 1,
168 col_utf16: 2
169 }
170 );
171 assert_eq!(
172 index.line_col(10.into()),
173 LineCol {
174 line: 1,
175 col_utf16: 4
176 }
177 );
178 assert_eq!(
179 index.line_col(11.into()),
180 LineCol {
181 line: 1,
182 col_utf16: 5
183 } 143 }
184 ); 144 if c == '\n' {
185 assert_eq!( 145 res.line += 1;
186 index.line_col(12.into()), 146 res.col_utf16 = 0;
187 LineCol { 147 } else {
188 line: 1, 148 res.col_utf16 += 1;
189 col_utf16: 6
190 } 149 }
191 ); 150 }
151 res
152}
192 153
193 let text = "\nhello\nworld"; 154#[cfg(test)]
194 let index = LineIndex::new(text); 155mod test_line_index {
195 assert_eq!( 156 use super::*;
196 index.line_col(0.into()), 157 use proptest::{prelude::*, proptest, proptest_helper};
197 LineCol { 158 use ra_text_edit::test_utils::{arb_text, arb_offset};
159
160 #[test]
161 fn test_line_index() {
162 let text = "hello\nworld";
163 let index = LineIndex::new(text);
164 assert_eq!(
165 index.line_col(0.into()),
166 LineCol {
167 line: 0,
168 col_utf16: 0
169 }
170 );
171 assert_eq!(
172 index.line_col(1.into()),
173 LineCol {
174 line: 0,
175 col_utf16: 1
176 }
177 );
178 assert_eq!(
179 index.line_col(5.into()),
180 LineCol {
181 line: 0,
182 col_utf16: 5
183 }
184 );
185 assert_eq!(
186 index.line_col(6.into()),
187 LineCol {
188 line: 1,
189 col_utf16: 0
190 }
191 );
192 assert_eq!(
193 index.line_col(7.into()),
194 LineCol {
195 line: 1,
196 col_utf16: 1
197 }
198 );
199 assert_eq!(
200 index.line_col(8.into()),
201 LineCol {
202 line: 1,
203 col_utf16: 2
204 }
205 );
206 assert_eq!(
207 index.line_col(10.into()),
208 LineCol {
209 line: 1,
210 col_utf16: 4
211 }
212 );
213 assert_eq!(
214 index.line_col(11.into()),
215 LineCol {
216 line: 1,
217 col_utf16: 5
218 }
219 );
220 assert_eq!(
221 index.line_col(12.into()),
222 LineCol {
223 line: 1,
224 col_utf16: 6
225 }
226 );
227
228 let text = "\nhello\nworld";
229 let index = LineIndex::new(text);
230 assert_eq!(
231 index.line_col(0.into()),
232 LineCol {
233 line: 0,
234 col_utf16: 0
235 }
236 );
237 assert_eq!(
238 index.line_col(1.into()),
239 LineCol {
240 line: 1,
241 col_utf16: 0
242 }
243 );
244 assert_eq!(
245 index.line_col(2.into()),
246 LineCol {
247 line: 1,
248 col_utf16: 1
249 }
250 );
251 assert_eq!(
252 index.line_col(6.into()),
253 LineCol {
254 line: 1,
255 col_utf16: 5
256 }
257 );
258 assert_eq!(
259 index.line_col(7.into()),
260 LineCol {
261 line: 2,
262 col_utf16: 0
263 }
264 );
265 }
266
267 fn arb_text_with_offset() -> BoxedStrategy<(TextUnit, String)> {
268 arb_text()
269 .prop_flat_map(|text| (arb_offset(&text), Just(text)))
270 .boxed()
271 }
272
273 fn to_line_col(text: &str, offset: TextUnit) -> LineCol {
274 let mut res = LineCol {
198 line: 0, 275 line: 0,
199 col_utf16: 0 276 col_utf16: 0,
200 } 277 };
201 ); 278 for (i, c) in text.char_indices() {
202 assert_eq!( 279 if i + c.len_utf8() > offset.to_usize() {
203 index.line_col(1.into()), 280 // if it's an invalid offset, inside a multibyte char
204 LineCol { 281 // return as if it was at the start of the char
205 line: 1, 282 break;
206 col_utf16: 0 283 }
207 } 284 if c == '\n' {
208 ); 285 res.line += 1;
209 assert_eq!( 286 res.col_utf16 = 0;
210 index.line_col(2.into()), 287 } else {
211 LineCol { 288 res.col_utf16 += 1;
212 line: 1, 289 }
213 col_utf16: 1
214 }
215 );
216 assert_eq!(
217 index.line_col(6.into()),
218 LineCol {
219 line: 1,
220 col_utf16: 5
221 } 290 }
222 ); 291 res
223 assert_eq!( 292 }
224 index.line_col(7.into()), 293
225 LineCol { 294 proptest! {
226 line: 2, 295 #[test]
227 col_utf16: 0 296 fn test_line_index_proptest((offset, text) in arb_text_with_offset()) {
297 let expected = to_line_col(&text, offset);
298 let line_index = LineIndex::new(&text);
299 let actual = line_index.line_col(offset);
300
301 assert_eq!(actual, expected);
228 } 302 }
229 ); 303 }
230} 304}
231 305
232#[cfg(test)] 306#[cfg(test)]
@@ -321,4 +395,5 @@ const C: char = \"メ メ\";
321 395
322 assert_eq!(col_index.utf16_to_utf8_col(2, 15), TextUnit::from_usize(15)); 396 assert_eq!(col_index.utf16_to_utf8_col(2, 15), TextUnit::from_usize(15));
323 } 397 }
398
324} 399}