diff options
Diffstat (limited to 'crates')
-rw-r--r-- | crates/ra_editor/src/line_index.rs | 28 | ||||
-rw-r--r-- | crates/ra_editor/src/line_index_utils.rs | 103 |
2 files changed, 92 insertions, 39 deletions
diff --git a/crates/ra_editor/src/line_index.rs b/crates/ra_editor/src/line_index.rs index 6dbabd97e..b01760313 100644 --- a/crates/ra_editor/src/line_index.rs +++ b/crates/ra_editor/src/line_index.rs | |||
@@ -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,6 +128,28 @@ impl LineIndex { | |||
122 | } | 128 | } |
123 | } | 129 | } |
124 | 130 | ||
131 | // for bench and test | ||
132 | pub fn to_line_col(text: &str, offset: TextUnit) -> LineCol { | ||
133 | let mut res = LineCol { | ||
134 | line: 0, | ||
135 | col_utf16: 0, | ||
136 | }; | ||
137 | for (i, c) in text.char_indices() { | ||
138 | if i + c.len_utf8() > offset.to_usize() { | ||
139 | // if it's an invalid offset, inside a multibyte char | ||
140 | // return as if it was at the start of the char | ||
141 | break; | ||
142 | } | ||
143 | if c == '\n' { | ||
144 | res.line += 1; | ||
145 | res.col_utf16 = 0; | ||
146 | } else { | ||
147 | res.col_utf16 += 1; | ||
148 | } | ||
149 | } | ||
150 | res | ||
151 | } | ||
152 | |||
125 | #[test] | 153 | #[test] |
126 | fn test_line_index() { | 154 | fn test_line_index() { |
127 | let text = "hello\nworld"; | 155 | let text = "hello\nworld"; |
diff --git a/crates/ra_editor/src/line_index_utils.rs b/crates/ra_editor/src/line_index_utils.rs index 382f3ac72..913ca5b15 100644 --- a/crates/ra_editor/src/line_index_utils.rs +++ b/crates/ra_editor/src/line_index_utils.rs | |||
@@ -1,6 +1,6 @@ | |||
1 | use ra_text_edit::AtomTextEdit; | 1 | use ra_text_edit::AtomTextEdit; |
2 | use ra_syntax::{TextUnit, TextRange}; | 2 | use ra_syntax::{TextUnit, TextRange}; |
3 | use crate::{LineIndex, LineCol, line_index::Utf16Char}; | 3 | use crate::{LineIndex, LineCol, line_index::Utf16Char, line_index}; |
4 | use superslice::Ext; | 4 | use superslice::Ext; |
5 | 5 | ||
6 | #[derive(Debug, Clone)] | 6 | #[derive(Debug, Clone)] |
@@ -154,7 +154,7 @@ impl<'a, 'b> Edits<'a, 'b> { | |||
154 | fn next_step(&mut self, step: &Step) -> NextNewlines { | 154 | fn next_step(&mut self, step: &Step) -> NextNewlines { |
155 | let step_pos = match step { | 155 | let step_pos = match step { |
156 | &Step::Newline(n) => n, | 156 | &Step::Newline(n) => n, |
157 | &Step::Utf16Char(r) => r.start(), | 157 | &Step::Utf16Char(r) => r.end(), |
158 | }; | 158 | }; |
159 | let res = match &mut self.current { | 159 | let res = match &mut self.current { |
160 | Some(edit) => { | 160 | Some(edit) => { |
@@ -214,6 +214,40 @@ impl<'a, 'b> Edits<'a, 'b> { | |||
214 | } | 214 | } |
215 | } | 215 | } |
216 | 216 | ||
217 | #[derive(Debug)] | ||
218 | struct RunningLineCol { | ||
219 | line: u32, | ||
220 | last_newline: TextUnit, | ||
221 | col_adjust: TextUnit, | ||
222 | } | ||
223 | |||
224 | impl RunningLineCol { | ||
225 | fn new() -> RunningLineCol { | ||
226 | RunningLineCol { | ||
227 | line: 0, | ||
228 | last_newline: TextUnit::from(0), | ||
229 | col_adjust: TextUnit::from(0), | ||
230 | } | ||
231 | } | ||
232 | |||
233 | fn to_line_col(&self, offset: TextUnit) -> LineCol { | ||
234 | LineCol { | ||
235 | line: self.line, | ||
236 | col_utf16: ((offset - self.last_newline) - self.col_adjust).into(), | ||
237 | } | ||
238 | } | ||
239 | |||
240 | fn add_line(&mut self, newline: TextUnit) { | ||
241 | self.line += 1; | ||
242 | self.last_newline = newline; | ||
243 | self.col_adjust = TextUnit::from(0); | ||
244 | } | ||
245 | |||
246 | fn adjust_col(&mut self, range: &TextRange) { | ||
247 | self.col_adjust += range.len() - TextUnit::from(1); | ||
248 | } | ||
249 | } | ||
250 | |||
217 | pub fn translate_offset_with_edit( | 251 | pub fn translate_offset_with_edit( |
218 | line_index: &LineIndex, | 252 | line_index: &LineIndex, |
219 | offset: TextUnit, | 253 | offset: TextUnit, |
@@ -228,49 +262,35 @@ pub fn translate_offset_with_edit( | |||
228 | 262 | ||
229 | let mut state = Edits::new(&sorted_edits); | 263 | let mut state = Edits::new(&sorted_edits); |
230 | 264 | ||
231 | let mut pos: LineCol = LineCol { | 265 | let mut res = RunningLineCol::new(); |
232 | line: 0, | ||
233 | col_utf16: 0, | ||
234 | }; | ||
235 | |||
236 | let mut last_newline: TextUnit = TextUnit::from(0); | ||
237 | let mut col_adjust: TextUnit = TextUnit::from(0); | ||
238 | 266 | ||
239 | macro_rules! test_step { | 267 | macro_rules! test_step { |
240 | ($x:ident) => { | 268 | ($x:ident) => { |
241 | match &$x { | 269 | match &$x { |
242 | Step::Newline(n) => { | 270 | Step::Newline(n) => { |
243 | if offset < *n { | 271 | if offset < *n { |
244 | return_pos!(); | 272 | return res.to_line_col(offset); |
245 | } else if offset == *n { | 273 | } else if offset == *n { |
246 | pos.line += 1; | 274 | res.add_line(*n); |
247 | pos.col_utf16 = 0; | 275 | return res.to_line_col(offset); |
248 | return pos; | ||
249 | } else { | 276 | } else { |
250 | pos.line += 1; | 277 | res.add_line(*n); |
251 | pos.col_utf16 = 0; | ||
252 | last_newline = *n; | ||
253 | col_adjust = TextUnit::from(0); | ||
254 | } | 278 | } |
255 | } | 279 | } |
256 | Step::Utf16Char(x) => { | 280 | Step::Utf16Char(x) => { |
257 | if offset < x.end() { | 281 | if offset < x.end() { |
258 | return_pos!(); | 282 | // if the offset is inside a multibyte char it's invalid |
283 | // clamp it to the start of the char | ||
284 | let clamp = offset.min(x.start()); | ||
285 | return res.to_line_col(clamp); | ||
259 | } else { | 286 | } else { |
260 | col_adjust += x.len() - TextUnit::from(1); | 287 | res.adjust_col(x); |
261 | } | 288 | } |
262 | } | 289 | } |
263 | } | 290 | } |
264 | }; | 291 | }; |
265 | } | 292 | } |
266 | 293 | ||
267 | macro_rules! return_pos { | ||
268 | () => { | ||
269 | pos.col_utf16 = ((offset - last_newline) - col_adjust).into(); | ||
270 | return pos; | ||
271 | }; | ||
272 | } | ||
273 | |||
274 | for orig_step in LineIndexStepIter::from(line_index) { | 294 | for orig_step in LineIndexStepIter::from(line_index) { |
275 | loop { | 295 | loop { |
276 | let translated_step = state.translate_step(&orig_step); | 296 | let translated_step = state.translate_step(&orig_step); |
@@ -305,7 +325,7 @@ pub fn translate_offset_with_edit( | |||
305 | } | 325 | } |
306 | } | 326 | } |
307 | 327 | ||
308 | return_pos!(); | 328 | res.to_line_col(offset) |
309 | } | 329 | } |
310 | 330 | ||
311 | // for bench | 331 | // for bench |
@@ -315,8 +335,7 @@ pub fn translate_after_edit( | |||
315 | edits: Vec<AtomTextEdit>, | 335 | edits: Vec<AtomTextEdit>, |
316 | ) -> LineCol { | 336 | ) -> LineCol { |
317 | let text = edit_text(pre_edit_text, edits); | 337 | let text = edit_text(pre_edit_text, edits); |
318 | let line_index = LineIndex::new(&text); | 338 | line_index::to_line_col(&text, offset) |
319 | line_index.line_col(offset) | ||
320 | } | 339 | } |
321 | 340 | ||
322 | fn edit_text(pre_edit_text: &str, mut edits: Vec<AtomTextEdit>) -> String { | 341 | fn edit_text(pre_edit_text: &str, mut edits: Vec<AtomTextEdit>) -> String { |
@@ -343,6 +362,7 @@ mod test { | |||
343 | #[derive(Debug)] | 362 | #[derive(Debug)] |
344 | struct ArbTextWithOffsetAndEdits { | 363 | struct ArbTextWithOffsetAndEdits { |
345 | text: String, | 364 | text: String, |
365 | edited_text: String, | ||
346 | offset: TextUnit, | 366 | offset: TextUnit, |
347 | edits: Vec<AtomTextEdit>, | 367 | edits: Vec<AtomTextEdit>, |
348 | } | 368 | } |
@@ -350,13 +370,18 @@ mod test { | |||
350 | fn arb_text_with_offset_and_edits() -> BoxedStrategy<ArbTextWithOffsetAndEdits> { | 370 | fn arb_text_with_offset_and_edits() -> BoxedStrategy<ArbTextWithOffsetAndEdits> { |
351 | arb_text() | 371 | arb_text() |
352 | .prop_flat_map(|text| { | 372 | .prop_flat_map(|text| { |
353 | (arb_offset(&text), arb_edits(&text), Just(text)).prop_map( | 373 | (arb_edits(&text), Just(text)).prop_flat_map(|(edits, text)| { |
354 | |(offset, edits, text)| ArbTextWithOffsetAndEdits { | 374 | let edited_text = edit_text(&text, edits.clone()); |
355 | text, | 375 | let arb_offset = arb_offset(&edited_text); |
356 | offset, | 376 | (Just(text), Just(edited_text), Just(edits), arb_offset).prop_map( |
357 | edits, | 377 | |(text, edited_text, edits, offset)| ArbTextWithOffsetAndEdits { |
358 | }, | 378 | text, |
359 | ) | 379 | edits, |
380 | edited_text, | ||
381 | offset, | ||
382 | }, | ||
383 | ) | ||
384 | }) | ||
360 | }) | 385 | }) |
361 | .boxed() | 386 | .boxed() |
362 | } | 387 | } |
@@ -364,11 +389,11 @@ mod test { | |||
364 | proptest! { | 389 | proptest! { |
365 | #[test] | 390 | #[test] |
366 | fn test_translate_offset_with_edit(x in arb_text_with_offset_and_edits()) { | 391 | fn test_translate_offset_with_edit(x in arb_text_with_offset_and_edits()) { |
392 | let expected = line_index::to_line_col(&x.edited_text, x.offset); | ||
367 | let line_index = LineIndex::new(&x.text); | 393 | let line_index = LineIndex::new(&x.text); |
368 | let expected = translate_after_edit(&x.text, x.offset, x.edits.clone()); | ||
369 | let actual = translate_offset_with_edit(&line_index, x.offset, &x.edits); | 394 | let actual = translate_offset_with_edit(&line_index, x.offset, &x.edits); |
370 | // assert_eq!(actual, expected); | 395 | |
371 | assert_eq!(actual.line, expected.line); | 396 | assert_eq!(actual, expected); |
372 | } | 397 | } |
373 | } | 398 | } |
374 | } | 399 | } |