diff options
Diffstat (limited to 'crates')
| -rw-r--r-- | crates/ra_editor/src/line_index.rs | 6 | ||||
| -rw-r--r-- | crates/ra_lsp_server/src/conv.rs | 270 |
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 @@ | |||
| 1 | use crate::TextUnit; | 1 | use crate::{TextUnit, TextRange}; |
| 2 | use rustc_hash::FxHashMap; | 2 | use rustc_hash::FxHashMap; |
| 3 | use superslice::Ext; | 3 | use 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)] | ||
| 300 | struct OffsetNewlineIter<'a> { | ||
| 301 | text: &'a str, | ||
| 302 | offset: TextUnit, | ||
| 303 | } | ||
| 304 | |||
| 305 | impl<'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)] | ||
| 321 | enum TranslatedPos { | ||
| 322 | Before, | ||
| 323 | After, | ||
| 324 | } | ||
| 325 | |||
| 326 | /// None means it was deleted | ||
| 327 | type TranslatedOffset = Option<(TranslatedPos, TextUnit)>; | ||
| 328 | |||
| 329 | fn 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 | |||
| 341 | trait 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 | |||
| 355 | struct TranslatedAtomEdit<'a> { | ||
| 356 | delete: TextRange, | ||
| 357 | insert: &'a str, | ||
| 358 | } | ||
| 359 | |||
| 360 | struct TranslatedNewlines<'a, T: TranslatedNewlineIterator> { | ||
| 361 | inner: T, | ||
| 362 | next_inner: Option<TranslatedOffset>, | ||
| 363 | edit: TranslatedAtomEdit<'a>, | ||
| 364 | insert: OffsetNewlineIter<'a>, | ||
| 365 | } | ||
| 366 | |||
| 367 | impl<'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 | |||
| 395 | impl<'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 | |||
| 424 | impl<'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 | |||
| 431 | impl<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 | |||
| 440 | struct IteratorWrapper<T: Iterator<Item = TextUnit>>(T); | ||
| 441 | |||
| 442 | impl<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 | |||
| 451 | impl<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 | |||
| 458 | fn 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)] | ||
| 469 | fn 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 | |||
| 299 | impl TryConvWith for SourceFileEdit { | 495 | impl 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 | } |
