aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/ra_editor/src/line_index.rs6
-rw-r--r--crates/ra_lsp_server/src/conv.rs270
2 files changed, 262 insertions, 14 deletions
diff --git a/crates/ra_editor/src/line_index.rs b/crates/ra_editor/src/line_index.rs
index aab7e4081..7eddfd502 100644
--- a/crates/ra_editor/src/line_index.rs
+++ b/crates/ra_editor/src/line_index.rs
@@ -1,4 +1,4 @@
1use crate::TextUnit; 1use crate::{TextUnit, TextRange};
2use rustc_hash::FxHashMap; 2use rustc_hash::FxHashMap;
3use superslice::Ext; 3use superslice::Ext;
4 4
@@ -120,6 +120,10 @@ impl LineIndex {
120 120
121 col 121 col
122 } 122 }
123
124 pub fn newlines(&self) -> &[TextUnit] {
125 &self.newlines[1..]
126 }
123} 127}
124 128
125#[test] 129#[test]
diff --git a/crates/ra_lsp_server/src/conv.rs b/crates/ra_lsp_server/src/conv.rs
index 1db499175..6d0ebbcd9 100644
--- a/crates/ra_lsp_server/src/conv.rs
+++ b/crates/ra_lsp_server/src/conv.rs
@@ -296,6 +296,202 @@ fn translate_offset_with_edit(
296 } 296 }
297} 297}
298 298
299#[derive(Debug)]
300struct OffsetNewlineIter<'a> {
301 text: &'a str,
302 offset: TextUnit,
303}
304
305impl<'a> Iterator for OffsetNewlineIter<'a> {
306 type Item = TextUnit;
307 fn next(&mut self) -> Option<TextUnit> {
308 let next_idx = self
309 .text
310 .char_indices()
311 .filter_map(|(i, c)| if c == '\n' { Some(i + 1) } else { None })
312 .next()?;
313 let next = self.offset + TextUnit::from_usize(next_idx);
314 self.text = &self.text[next_idx..];
315 self.offset = next;
316 Some(next)
317 }
318}
319
320#[derive(Debug, Clone, Copy, PartialEq)]
321enum TranslatedPos {
322 Before,
323 After,
324}
325
326/// None means it was deleted
327type TranslatedOffset = Option<(TranslatedPos, TextUnit)>;
328
329fn translate_offset(offset: TextUnit, edit: &TranslatedAtomEdit) -> TranslatedOffset {
330 if offset <= edit.delete.start() {
331 Some((TranslatedPos::Before, offset))
332 } else if offset <= edit.delete.end() {
333 None
334 } else {
335 let diff = edit.insert.len() as i64 - edit.delete.len().to_usize() as i64;
336 let after = TextUnit::from((offset.to_usize() as i64 + diff) as u32);
337 Some((TranslatedPos::After, after))
338 }
339}
340
341trait TranslatedNewlineIterator {
342 fn translate(&self, offset: TextUnit) -> TextUnit;
343 fn translate_range(&self, range: TextRange) -> TextRange {
344 TextRange::from_to(self.translate(range.start()), self.translate(range.end()))
345 }
346 fn next_translated(&mut self) -> Option<TextUnit>;
347 fn boxed<'a>(self) -> Box<TranslatedNewlineIterator + 'a>
348 where
349 Self: 'a + Sized,
350 {
351 Box::new(self)
352 }
353}
354
355struct TranslatedAtomEdit<'a> {
356 delete: TextRange,
357 insert: &'a str,
358}
359
360struct TranslatedNewlines<'a, T: TranslatedNewlineIterator> {
361 inner: T,
362 next_inner: Option<TranslatedOffset>,
363 edit: TranslatedAtomEdit<'a>,
364 insert: OffsetNewlineIter<'a>,
365}
366
367impl<'a, T: TranslatedNewlineIterator> TranslatedNewlines<'a, T> {
368 fn from(inner: T, edit: &'a AtomTextEdit) -> Self {
369 let delete = inner.translate_range(edit.delete);
370 let mut res = TranslatedNewlines {
371 inner,
372 next_inner: None,
373 edit: TranslatedAtomEdit {
374 delete,
375 insert: &edit.insert,
376 },
377 insert: OffsetNewlineIter {
378 offset: delete.start(),
379 text: &edit.insert,
380 },
381 };
382 // prepare next_inner
383 res.advance_inner();
384 res
385 }
386
387 fn advance_inner(&mut self) {
388 self.next_inner = self
389 .inner
390 .next_translated()
391 .map(|x| translate_offset(x, &self.edit));
392 }
393}
394
395impl<'a, T: TranslatedNewlineIterator> TranslatedNewlineIterator for TranslatedNewlines<'a, T> {
396 fn translate(&self, offset: TextUnit) -> TextUnit {
397 let offset = self.inner.translate(offset);
398 let (_, offset) =
399 translate_offset(offset, &self.edit).expect("translate_unit returned None");
400 offset
401 }
402
403 fn next_translated(&mut self) -> Option<TextUnit> {
404 match self.next_inner {
405 None => self.insert.next(),
406 Some(next) => match next {
407 None => self.insert.next().or_else(|| {
408 self.advance_inner();
409 self.next_translated()
410 }),
411 Some((TranslatedPos::Before, next)) => {
412 self.advance_inner();
413 Some(next)
414 }
415 Some((TranslatedPos::After, next)) => self.insert.next().or_else(|| {
416 self.advance_inner();
417 Some(next)
418 }),
419 },
420 }
421 }
422}
423
424impl<'a> Iterator for Box<dyn TranslatedNewlineIterator + 'a> {
425 type Item = TextUnit;
426 fn next(&mut self) -> Option<TextUnit> {
427 self.next_translated()
428 }
429}
430
431impl<T: TranslatedNewlineIterator + ?Sized> TranslatedNewlineIterator for Box<T> {
432 fn translate(&self, offset: TextUnit) -> TextUnit {
433 self.as_ref().translate(offset)
434 }
435 fn next_translated(&mut self) -> Option<TextUnit> {
436 self.as_mut().next_translated()
437 }
438}
439
440struct IteratorWrapper<T: Iterator<Item = TextUnit>>(T);
441
442impl<T: Iterator<Item = TextUnit>> TranslatedNewlineIterator for IteratorWrapper<T> {
443 fn translate(&self, offset: TextUnit) -> TextUnit {
444 offset
445 }
446 fn next_translated(&mut self) -> Option<TextUnit> {
447 self.0.next()
448 }
449}
450
451impl<T: Iterator<Item = TextUnit>> Iterator for IteratorWrapper<T> {
452 type Item = TextUnit;
453 fn next(&mut self) -> Option<TextUnit> {
454 self.0.next()
455 }
456}
457
458fn translate_newlines<'a>(
459 mut newlines: Box<TranslatedNewlineIterator + 'a>,
460 edits: &'a [AtomTextEdit],
461) -> Box<TranslatedNewlineIterator + 'a> {
462 for edit in edits {
463 newlines = TranslatedNewlines::from(newlines, edit).boxed();
464 }
465 newlines
466}
467
468#[allow(dead_code)]
469fn translate_offset_with_edit_fast(
470 pre_edit_index: &LineIndex,
471 offset: TextUnit,
472 edits: &[AtomTextEdit],
473) -> LineCol {
474 // println!("{:?}", pre_edit_index.newlines());
475 let mut newlines: Box<TranslatedNewlineIterator> = Box::new(IteratorWrapper(
476 pre_edit_index.newlines().iter().map(|x| *x),
477 ));
478
479 newlines = translate_newlines(newlines, edits);
480
481 let mut line = 0;
482 for n in newlines {
483 if n > offset {
484 break;
485 }
486 line += 1;
487 }
488
489 LineCol {
490 line: line,
491 col_utf16: 0,
492 }
493}
494
299impl TryConvWith for SourceFileEdit { 495impl TryConvWith for SourceFileEdit {
300 type Ctx = ServerWorld; 496 type Ctx = ServerWorld;
301 type Output = TextDocumentEdit; 497 type Output = TextDocumentEdit;
@@ -414,36 +610,84 @@ mod test {
414 .boxed() 610 .boxed()
415 } 611 }
416 612
417 fn translate_offset_with_edit_naive( 613 fn edit_text(pre_edit_text: &str, mut edits: Vec<AtomTextEdit>) -> String {
418 pre_edit_text: String,
419 offset: TextUnit,
420 edits: &[AtomTextEdit],
421 ) -> LineCol {
422 // apply edits ordered from last to first 614 // apply edits ordered from last to first
423 // since they should not overlap we can just use start() 615 // since they should not overlap we can just use start()
424 let mut edits: Vec<AtomTextEdit> = edits.to_vec();
425 edits.sort_by_key(|x| -(x.delete.start().to_usize() as isize)); 616 edits.sort_by_key(|x| -(x.delete.start().to_usize() as isize));
426 617
427 let mut text = pre_edit_text; 618 let mut text = pre_edit_text.to_owned();
428 619
429 for edit in &edits { 620 for edit in &edits {
430 let range = edit.delete.start().to_usize()..edit.delete.end().to_usize(); 621 let range = edit.delete.start().to_usize()..edit.delete.end().to_usize();
431 text.replace_range(range, &edit.insert); 622 text.replace_range(range, &edit.insert);
432 } 623 }
433 624
434 let line_index = LineIndex::new(&text); 625 text
626 }
435 627
628 fn translate_after_edit(
629 pre_edit_text: &str,
630 offset: TextUnit,
631 edits: Vec<AtomTextEdit>,
632 ) -> LineCol {
633 let text = edit_text(pre_edit_text, edits);
634 let line_index = LineIndex::new(&text);
436 line_index.line_col(offset) 635 line_index.line_col(offset)
437 } 636 }
438 637
439 proptest! { 638 proptest! {
440 #[test] 639 #[test]
441 fn test_translate_offset_with_edit(x in arb_text_with_offset_and_edits()) { 640 fn test_translate_offset_with_edit(x in arb_text_with_offset_and_edits()) {
442 if x.edits.len() <= 1 { 641 let line_index = LineIndex::new(&x.text);
443 let expected = translate_offset_with_edit_naive(x.text.clone(), x.offset, &x.edits); 642 let expected = translate_after_edit(&x.text, x.offset, x.edits.clone());
444 let actual = translate_offset_with_edit(&LineIndex::new(&x.text), x.offset, &x.edits); 643 let actual = translate_offset_with_edit_fast(&line_index, x.offset, &x.edits);
445 assert_eq!(actual, expected); 644 assert_eq!(actual.line, expected.line);
446 }
447 } 645 }
448 } 646 }
647
648 #[test]
649 fn test_translate_offset_with_edit_1() {
650 let x = ArbTextWithOffsetAndEdits {
651 text: "jbnan".to_owned(),
652 offset: 3.into(),
653 edits: vec![
654 AtomTextEdit::delete(TextRange::from_to(1.into(), 3.into())),
655 AtomTextEdit::insert(4.into(), "\n".into()),
656 ],
657 };
658 let line_index = LineIndex::new(&x.text);
659 let expected = translate_after_edit(&x.text, x.offset, x.edits.clone());
660 let actual = translate_offset_with_edit_fast(&line_index, x.offset, &x.edits);
661 // assert_eq!(actual, expected);
662 assert_eq!(actual.line, expected.line);
663 }
664
665 #[test]
666 fn test_translate_offset_with_edit_2() {
667 let x = ArbTextWithOffsetAndEdits {
668 text: "aa\n".to_owned(),
669 offset: 1.into(),
670 edits: vec![AtomTextEdit::delete(TextRange::from_to(0.into(), 2.into()))],
671 };
672 let line_index = LineIndex::new(&x.text);
673 let expected = translate_after_edit(&x.text, x.offset, x.edits.clone());
674 let actual = translate_offset_with_edit_fast(&line_index, x.offset, &x.edits);
675 // assert_eq!(actual, expected);
676 assert_eq!(actual.line, expected.line);
677 }
678
679 #[test]
680 fn test_translate_offset_with_edit_3() {
681 let x = ArbTextWithOffsetAndEdits {
682 text: "".to_owned(),
683 offset: 0.into(),
684 edits: vec![AtomTextEdit::insert(0.into(), "\n".into())],
685 };
686 let line_index = LineIndex::new(&x.text);
687 let expected = translate_after_edit(&x.text, x.offset, x.edits.clone());
688 let actual = translate_offset_with_edit_fast(&line_index, x.offset, &x.edits);
689 // assert_eq!(actual, expected);
690 assert_eq!(actual.line, expected.line);
691 }
692
449} 693}