From d9519791590170fd77aa45cc9a802539eaf16efc Mon Sep 17 00:00:00 2001 From: Bernardo Date: Fri, 14 Dec 2018 18:00:35 +0100 Subject: test translate_offset_with_edit against simple impl for single edits --- crates/ra_lsp_server/src/conv.rs | 61 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) (limited to 'crates/ra_lsp_server/src') diff --git a/crates/ra_lsp_server/src/conv.rs b/crates/ra_lsp_server/src/conv.rs index 051f1f995..1db499175 100644 --- a/crates/ra_lsp_server/src/conv.rs +++ b/crates/ra_lsp_server/src/conv.rs @@ -386,3 +386,64 @@ where self.iter.next().map(|item| item.conv_with(self.ctx)) } } + +#[cfg(test)] +mod test { + use proptest::{prelude::*, proptest, proptest_helper}; + use super::*; + use ra_text_edit::test_utils::{arb_text, arb_offset, arb_edits}; + + #[derive(Debug)] + struct ArbTextWithOffsetAndEdits { + text: String, + offset: TextUnit, + edits: Vec, + } + + fn arb_text_with_offset_and_edits() -> BoxedStrategy { + arb_text() + .prop_flat_map(|text| { + (arb_offset(&text), arb_edits(&text), Just(text)).prop_map( + |(offset, edits, text)| ArbTextWithOffsetAndEdits { + text, + offset, + edits, + }, + ) + }) + .boxed() + } + + fn translate_offset_with_edit_naive( + pre_edit_text: String, + offset: TextUnit, + edits: &[AtomTextEdit], + ) -> LineCol { + // apply edits ordered from last to first + // since they should not overlap we can just use start() + let mut edits: Vec = edits.to_vec(); + edits.sort_by_key(|x| -(x.delete.start().to_usize() as isize)); + + let mut text = pre_edit_text; + + for edit in &edits { + let range = edit.delete.start().to_usize()..edit.delete.end().to_usize(); + text.replace_range(range, &edit.insert); + } + + let line_index = LineIndex::new(&text); + + line_index.line_col(offset) + } + + proptest! { + #[test] + fn test_translate_offset_with_edit(x in arb_text_with_offset_and_edits()) { + if x.edits.len() <= 1 { + let expected = translate_offset_with_edit_naive(x.text.clone(), x.offset, &x.edits); + let actual = translate_offset_with_edit(&LineIndex::new(&x.text), x.offset, &x.edits); + assert_eq!(actual, expected); + } + } + } +} -- cgit v1.2.3 From 881c29192d39f657bf518baf399c47a5bfdc922f Mon Sep 17 00:00:00 2001 From: Bernardo Date: Mon, 17 Dec 2018 20:13:54 +0100 Subject: initial newline translation working todo: cleanup, simplify handle columns --- crates/ra_lsp_server/src/conv.rs | 270 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 257 insertions(+), 13 deletions(-) (limited to 'crates/ra_lsp_server/src') 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( } } +#[derive(Debug)] +struct OffsetNewlineIter<'a> { + text: &'a str, + offset: TextUnit, +} + +impl<'a> Iterator for OffsetNewlineIter<'a> { + type Item = TextUnit; + fn next(&mut self) -> Option { + let next_idx = self + .text + .char_indices() + .filter_map(|(i, c)| if c == '\n' { Some(i + 1) } else { None }) + .next()?; + let next = self.offset + TextUnit::from_usize(next_idx); + self.text = &self.text[next_idx..]; + self.offset = next; + Some(next) + } +} + +#[derive(Debug, Clone, Copy, PartialEq)] +enum TranslatedPos { + Before, + After, +} + +/// None means it was deleted +type TranslatedOffset = Option<(TranslatedPos, TextUnit)>; + +fn translate_offset(offset: TextUnit, edit: &TranslatedAtomEdit) -> TranslatedOffset { + if offset <= edit.delete.start() { + Some((TranslatedPos::Before, offset)) + } else if offset <= edit.delete.end() { + None + } else { + let diff = edit.insert.len() as i64 - edit.delete.len().to_usize() as i64; + let after = TextUnit::from((offset.to_usize() as i64 + diff) as u32); + Some((TranslatedPos::After, after)) + } +} + +trait TranslatedNewlineIterator { + fn translate(&self, offset: TextUnit) -> TextUnit; + fn translate_range(&self, range: TextRange) -> TextRange { + TextRange::from_to(self.translate(range.start()), self.translate(range.end())) + } + fn next_translated(&mut self) -> Option; + fn boxed<'a>(self) -> Box + where + Self: 'a + Sized, + { + Box::new(self) + } +} + +struct TranslatedAtomEdit<'a> { + delete: TextRange, + insert: &'a str, +} + +struct TranslatedNewlines<'a, T: TranslatedNewlineIterator> { + inner: T, + next_inner: Option, + edit: TranslatedAtomEdit<'a>, + insert: OffsetNewlineIter<'a>, +} + +impl<'a, T: TranslatedNewlineIterator> TranslatedNewlines<'a, T> { + fn from(inner: T, edit: &'a AtomTextEdit) -> Self { + let delete = inner.translate_range(edit.delete); + let mut res = TranslatedNewlines { + inner, + next_inner: None, + edit: TranslatedAtomEdit { + delete, + insert: &edit.insert, + }, + insert: OffsetNewlineIter { + offset: delete.start(), + text: &edit.insert, + }, + }; + // prepare next_inner + res.advance_inner(); + res + } + + fn advance_inner(&mut self) { + self.next_inner = self + .inner + .next_translated() + .map(|x| translate_offset(x, &self.edit)); + } +} + +impl<'a, T: TranslatedNewlineIterator> TranslatedNewlineIterator for TranslatedNewlines<'a, T> { + fn translate(&self, offset: TextUnit) -> TextUnit { + let offset = self.inner.translate(offset); + let (_, offset) = + translate_offset(offset, &self.edit).expect("translate_unit returned None"); + offset + } + + fn next_translated(&mut self) -> Option { + match self.next_inner { + None => self.insert.next(), + Some(next) => match next { + None => self.insert.next().or_else(|| { + self.advance_inner(); + self.next_translated() + }), + Some((TranslatedPos::Before, next)) => { + self.advance_inner(); + Some(next) + } + Some((TranslatedPos::After, next)) => self.insert.next().or_else(|| { + self.advance_inner(); + Some(next) + }), + }, + } + } +} + +impl<'a> Iterator for Box { + type Item = TextUnit; + fn next(&mut self) -> Option { + self.next_translated() + } +} + +impl TranslatedNewlineIterator for Box { + fn translate(&self, offset: TextUnit) -> TextUnit { + self.as_ref().translate(offset) + } + fn next_translated(&mut self) -> Option { + self.as_mut().next_translated() + } +} + +struct IteratorWrapper>(T); + +impl> TranslatedNewlineIterator for IteratorWrapper { + fn translate(&self, offset: TextUnit) -> TextUnit { + offset + } + fn next_translated(&mut self) -> Option { + self.0.next() + } +} + +impl> Iterator for IteratorWrapper { + type Item = TextUnit; + fn next(&mut self) -> Option { + self.0.next() + } +} + +fn translate_newlines<'a>( + mut newlines: Box, + edits: &'a [AtomTextEdit], +) -> Box { + for edit in edits { + newlines = TranslatedNewlines::from(newlines, edit).boxed(); + } + newlines +} + +#[allow(dead_code)] +fn translate_offset_with_edit_fast( + pre_edit_index: &LineIndex, + offset: TextUnit, + edits: &[AtomTextEdit], +) -> LineCol { + // println!("{:?}", pre_edit_index.newlines()); + let mut newlines: Box = Box::new(IteratorWrapper( + pre_edit_index.newlines().iter().map(|x| *x), + )); + + newlines = translate_newlines(newlines, edits); + + let mut line = 0; + for n in newlines { + if n > offset { + break; + } + line += 1; + } + + LineCol { + line: line, + col_utf16: 0, + } +} + impl TryConvWith for SourceFileEdit { type Ctx = ServerWorld; type Output = TextDocumentEdit; @@ -414,36 +610,84 @@ mod test { .boxed() } - fn translate_offset_with_edit_naive( - pre_edit_text: String, - offset: TextUnit, - edits: &[AtomTextEdit], - ) -> LineCol { + fn edit_text(pre_edit_text: &str, mut edits: Vec) -> String { // apply edits ordered from last to first // since they should not overlap we can just use start() - let mut edits: Vec = edits.to_vec(); edits.sort_by_key(|x| -(x.delete.start().to_usize() as isize)); - let mut text = pre_edit_text; + let mut text = pre_edit_text.to_owned(); for edit in &edits { let range = edit.delete.start().to_usize()..edit.delete.end().to_usize(); text.replace_range(range, &edit.insert); } - let line_index = LineIndex::new(&text); + text + } + fn translate_after_edit( + pre_edit_text: &str, + offset: TextUnit, + edits: Vec, + ) -> LineCol { + let text = edit_text(pre_edit_text, edits); + let line_index = LineIndex::new(&text); line_index.line_col(offset) } proptest! { #[test] fn test_translate_offset_with_edit(x in arb_text_with_offset_and_edits()) { - if x.edits.len() <= 1 { - let expected = translate_offset_with_edit_naive(x.text.clone(), x.offset, &x.edits); - let actual = translate_offset_with_edit(&LineIndex::new(&x.text), x.offset, &x.edits); - assert_eq!(actual, expected); - } + let line_index = LineIndex::new(&x.text); + let expected = translate_after_edit(&x.text, x.offset, x.edits.clone()); + let actual = translate_offset_with_edit_fast(&line_index, x.offset, &x.edits); + assert_eq!(actual.line, expected.line); } } + + #[test] + fn test_translate_offset_with_edit_1() { + let x = ArbTextWithOffsetAndEdits { + text: "jbnan".to_owned(), + offset: 3.into(), + edits: vec![ + AtomTextEdit::delete(TextRange::from_to(1.into(), 3.into())), + AtomTextEdit::insert(4.into(), "\n".into()), + ], + }; + let line_index = LineIndex::new(&x.text); + let expected = translate_after_edit(&x.text, x.offset, x.edits.clone()); + let actual = translate_offset_with_edit_fast(&line_index, x.offset, &x.edits); + // assert_eq!(actual, expected); + assert_eq!(actual.line, expected.line); + } + + #[test] + fn test_translate_offset_with_edit_2() { + let x = ArbTextWithOffsetAndEdits { + text: "aa\n".to_owned(), + offset: 1.into(), + edits: vec![AtomTextEdit::delete(TextRange::from_to(0.into(), 2.into()))], + }; + let line_index = LineIndex::new(&x.text); + let expected = translate_after_edit(&x.text, x.offset, x.edits.clone()); + let actual = translate_offset_with_edit_fast(&line_index, x.offset, &x.edits); + // assert_eq!(actual, expected); + assert_eq!(actual.line, expected.line); + } + + #[test] + fn test_translate_offset_with_edit_3() { + let x = ArbTextWithOffsetAndEdits { + text: "".to_owned(), + offset: 0.into(), + edits: vec![AtomTextEdit::insert(0.into(), "\n".into())], + }; + let line_index = LineIndex::new(&x.text); + let expected = translate_after_edit(&x.text, x.offset, x.edits.clone()); + let actual = translate_offset_with_edit_fast(&line_index, x.offset, &x.edits); + // assert_eq!(actual, expected); + assert_eq!(actual.line, expected.line); + } + } -- cgit v1.2.3 From 8c9df62c1c6a778a8df9ea028d1dce98c91c4d9d Mon Sep 17 00:00:00 2001 From: Bernardo Date: Tue, 18 Dec 2018 18:46:54 +0100 Subject: move translate_offset_with_edit to ra_editor --- crates/ra_lsp_server/src/conv.rs | 305 --------------------------------------- 1 file changed, 305 deletions(-) (limited to 'crates/ra_lsp_server/src') diff --git a/crates/ra_lsp_server/src/conv.rs b/crates/ra_lsp_server/src/conv.rs index 6d0ebbcd9..051f1f995 100644 --- a/crates/ra_lsp_server/src/conv.rs +++ b/crates/ra_lsp_server/src/conv.rs @@ -296,202 +296,6 @@ fn translate_offset_with_edit( } } -#[derive(Debug)] -struct OffsetNewlineIter<'a> { - text: &'a str, - offset: TextUnit, -} - -impl<'a> Iterator for OffsetNewlineIter<'a> { - type Item = TextUnit; - fn next(&mut self) -> Option { - let next_idx = self - .text - .char_indices() - .filter_map(|(i, c)| if c == '\n' { Some(i + 1) } else { None }) - .next()?; - let next = self.offset + TextUnit::from_usize(next_idx); - self.text = &self.text[next_idx..]; - self.offset = next; - Some(next) - } -} - -#[derive(Debug, Clone, Copy, PartialEq)] -enum TranslatedPos { - Before, - After, -} - -/// None means it was deleted -type TranslatedOffset = Option<(TranslatedPos, TextUnit)>; - -fn translate_offset(offset: TextUnit, edit: &TranslatedAtomEdit) -> TranslatedOffset { - if offset <= edit.delete.start() { - Some((TranslatedPos::Before, offset)) - } else if offset <= edit.delete.end() { - None - } else { - let diff = edit.insert.len() as i64 - edit.delete.len().to_usize() as i64; - let after = TextUnit::from((offset.to_usize() as i64 + diff) as u32); - Some((TranslatedPos::After, after)) - } -} - -trait TranslatedNewlineIterator { - fn translate(&self, offset: TextUnit) -> TextUnit; - fn translate_range(&self, range: TextRange) -> TextRange { - TextRange::from_to(self.translate(range.start()), self.translate(range.end())) - } - fn next_translated(&mut self) -> Option; - fn boxed<'a>(self) -> Box - where - Self: 'a + Sized, - { - Box::new(self) - } -} - -struct TranslatedAtomEdit<'a> { - delete: TextRange, - insert: &'a str, -} - -struct TranslatedNewlines<'a, T: TranslatedNewlineIterator> { - inner: T, - next_inner: Option, - edit: TranslatedAtomEdit<'a>, - insert: OffsetNewlineIter<'a>, -} - -impl<'a, T: TranslatedNewlineIterator> TranslatedNewlines<'a, T> { - fn from(inner: T, edit: &'a AtomTextEdit) -> Self { - let delete = inner.translate_range(edit.delete); - let mut res = TranslatedNewlines { - inner, - next_inner: None, - edit: TranslatedAtomEdit { - delete, - insert: &edit.insert, - }, - insert: OffsetNewlineIter { - offset: delete.start(), - text: &edit.insert, - }, - }; - // prepare next_inner - res.advance_inner(); - res - } - - fn advance_inner(&mut self) { - self.next_inner = self - .inner - .next_translated() - .map(|x| translate_offset(x, &self.edit)); - } -} - -impl<'a, T: TranslatedNewlineIterator> TranslatedNewlineIterator for TranslatedNewlines<'a, T> { - fn translate(&self, offset: TextUnit) -> TextUnit { - let offset = self.inner.translate(offset); - let (_, offset) = - translate_offset(offset, &self.edit).expect("translate_unit returned None"); - offset - } - - fn next_translated(&mut self) -> Option { - match self.next_inner { - None => self.insert.next(), - Some(next) => match next { - None => self.insert.next().or_else(|| { - self.advance_inner(); - self.next_translated() - }), - Some((TranslatedPos::Before, next)) => { - self.advance_inner(); - Some(next) - } - Some((TranslatedPos::After, next)) => self.insert.next().or_else(|| { - self.advance_inner(); - Some(next) - }), - }, - } - } -} - -impl<'a> Iterator for Box { - type Item = TextUnit; - fn next(&mut self) -> Option { - self.next_translated() - } -} - -impl TranslatedNewlineIterator for Box { - fn translate(&self, offset: TextUnit) -> TextUnit { - self.as_ref().translate(offset) - } - fn next_translated(&mut self) -> Option { - self.as_mut().next_translated() - } -} - -struct IteratorWrapper>(T); - -impl> TranslatedNewlineIterator for IteratorWrapper { - fn translate(&self, offset: TextUnit) -> TextUnit { - offset - } - fn next_translated(&mut self) -> Option { - self.0.next() - } -} - -impl> Iterator for IteratorWrapper { - type Item = TextUnit; - fn next(&mut self) -> Option { - self.0.next() - } -} - -fn translate_newlines<'a>( - mut newlines: Box, - edits: &'a [AtomTextEdit], -) -> Box { - for edit in edits { - newlines = TranslatedNewlines::from(newlines, edit).boxed(); - } - newlines -} - -#[allow(dead_code)] -fn translate_offset_with_edit_fast( - pre_edit_index: &LineIndex, - offset: TextUnit, - edits: &[AtomTextEdit], -) -> LineCol { - // println!("{:?}", pre_edit_index.newlines()); - let mut newlines: Box = Box::new(IteratorWrapper( - pre_edit_index.newlines().iter().map(|x| *x), - )); - - newlines = translate_newlines(newlines, edits); - - let mut line = 0; - for n in newlines { - if n > offset { - break; - } - line += 1; - } - - LineCol { - line: line, - col_utf16: 0, - } -} - impl TryConvWith for SourceFileEdit { type Ctx = ServerWorld; type Output = TextDocumentEdit; @@ -582,112 +386,3 @@ where self.iter.next().map(|item| item.conv_with(self.ctx)) } } - -#[cfg(test)] -mod test { - use proptest::{prelude::*, proptest, proptest_helper}; - use super::*; - use ra_text_edit::test_utils::{arb_text, arb_offset, arb_edits}; - - #[derive(Debug)] - struct ArbTextWithOffsetAndEdits { - text: String, - offset: TextUnit, - edits: Vec, - } - - fn arb_text_with_offset_and_edits() -> BoxedStrategy { - arb_text() - .prop_flat_map(|text| { - (arb_offset(&text), arb_edits(&text), Just(text)).prop_map( - |(offset, edits, text)| ArbTextWithOffsetAndEdits { - text, - offset, - edits, - }, - ) - }) - .boxed() - } - - fn edit_text(pre_edit_text: &str, mut edits: Vec) -> String { - // apply edits ordered from last to first - // since they should not overlap we can just use start() - edits.sort_by_key(|x| -(x.delete.start().to_usize() as isize)); - - let mut text = pre_edit_text.to_owned(); - - for edit in &edits { - let range = edit.delete.start().to_usize()..edit.delete.end().to_usize(); - text.replace_range(range, &edit.insert); - } - - text - } - - fn translate_after_edit( - pre_edit_text: &str, - offset: TextUnit, - edits: Vec, - ) -> LineCol { - let text = edit_text(pre_edit_text, edits); - let line_index = LineIndex::new(&text); - line_index.line_col(offset) - } - - proptest! { - #[test] - fn test_translate_offset_with_edit(x in arb_text_with_offset_and_edits()) { - let line_index = LineIndex::new(&x.text); - let expected = translate_after_edit(&x.text, x.offset, x.edits.clone()); - let actual = translate_offset_with_edit_fast(&line_index, x.offset, &x.edits); - assert_eq!(actual.line, expected.line); - } - } - - #[test] - fn test_translate_offset_with_edit_1() { - let x = ArbTextWithOffsetAndEdits { - text: "jbnan".to_owned(), - offset: 3.into(), - edits: vec![ - AtomTextEdit::delete(TextRange::from_to(1.into(), 3.into())), - AtomTextEdit::insert(4.into(), "\n".into()), - ], - }; - let line_index = LineIndex::new(&x.text); - let expected = translate_after_edit(&x.text, x.offset, x.edits.clone()); - let actual = translate_offset_with_edit_fast(&line_index, x.offset, &x.edits); - // assert_eq!(actual, expected); - assert_eq!(actual.line, expected.line); - } - - #[test] - fn test_translate_offset_with_edit_2() { - let x = ArbTextWithOffsetAndEdits { - text: "aa\n".to_owned(), - offset: 1.into(), - edits: vec![AtomTextEdit::delete(TextRange::from_to(0.into(), 2.into()))], - }; - let line_index = LineIndex::new(&x.text); - let expected = translate_after_edit(&x.text, x.offset, x.edits.clone()); - let actual = translate_offset_with_edit_fast(&line_index, x.offset, &x.edits); - // assert_eq!(actual, expected); - assert_eq!(actual.line, expected.line); - } - - #[test] - fn test_translate_offset_with_edit_3() { - let x = ArbTextWithOffsetAndEdits { - text: "".to_owned(), - offset: 0.into(), - edits: vec![AtomTextEdit::insert(0.into(), "\n".into())], - }; - let line_index = LineIndex::new(&x.text); - let expected = translate_after_edit(&x.text, x.offset, x.edits.clone()); - let actual = translate_offset_with_edit_fast(&line_index, x.offset, &x.edits); - // assert_eq!(actual, expected); - assert_eq!(actual.line, expected.line); - } - -} -- cgit v1.2.3 From 6b2da4e5474ec064e44b7cf19523de1bab72ba27 Mon Sep 17 00:00:00 2001 From: Bernardo Date: Sun, 23 Dec 2018 15:49:14 +0100 Subject: use new translate_offset_with_edit for TryConvWith doc comments --- crates/ra_lsp_server/src/conv.rs | 37 +------------------------------------ 1 file changed, 1 insertion(+), 36 deletions(-) (limited to 'crates/ra_lsp_server/src') diff --git a/crates/ra_lsp_server/src/conv.rs b/crates/ra_lsp_server/src/conv.rs index 051f1f995..63827aeea 100644 --- a/crates/ra_lsp_server/src/conv.rs +++ b/crates/ra_lsp_server/src/conv.rs @@ -3,7 +3,7 @@ use languageserver_types::{ TextDocumentItem, TextDocumentPositionParams, Url, VersionedTextDocumentIdentifier, InsertTextFormat, }; use ra_analysis::{FileId, FileSystemEdit, SourceChange, SourceFileEdit, FilePosition, CompletionItem, CompletionItemKind, InsertText}; -use ra_editor::{LineCol, LineIndex}; +use ra_editor::{LineCol, LineIndex, translate_offset_with_edit}; use ra_text_edit::{AtomTextEdit, TextEdit}; use ra_syntax::{SyntaxKind, TextRange, TextUnit}; @@ -261,41 +261,6 @@ impl TryConvWith for SourceChange { } } -// HACK: we should translate offset to line/column using linde_index *with edits applied*. -// A naive version of this function would be to apply `edits` to the original text, -// construct a new line index and use that, but it would be slow. -// -// Writing fast & correct version is issue #105, let's use a quick hack in the meantime -fn translate_offset_with_edit( - pre_edit_index: &LineIndex, - offset: TextUnit, - edits: &[AtomTextEdit], -) -> LineCol { - let fallback = pre_edit_index.line_col(offset); - let edit = match edits.first() { - None => return fallback, - Some(edit) => edit, - }; - let end_offset = edit.delete.start() + TextUnit::of_str(&edit.insert); - if !(edit.delete.start() <= offset && offset <= end_offset) { - return fallback; - } - let rel_offset = offset - edit.delete.start(); - let in_edit_line_col = LineIndex::new(&edit.insert).line_col(rel_offset); - let edit_line_col = pre_edit_index.line_col(edit.delete.start()); - if in_edit_line_col.line == 0 { - LineCol { - line: edit_line_col.line, - col_utf16: edit_line_col.col_utf16 + in_edit_line_col.col_utf16, - } - } else { - LineCol { - line: edit_line_col.line + in_edit_line_col.line, - col_utf16: in_edit_line_col.col_utf16, - } - } -} - impl TryConvWith for SourceFileEdit { type Ctx = ServerWorld; type Output = TextDocumentEdit; -- cgit v1.2.3 From e9c186e48a77b536053c0f75ac9ea5b2fd322cfa Mon Sep 17 00:00:00 2001 From: Bernardo Date: Tue, 25 Dec 2018 20:49:18 +0100 Subject: change to `TextEdit` to avoid allocation and sort rename newline to step where applicable --- crates/ra_lsp_server/src/conv.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'crates/ra_lsp_server/src') diff --git a/crates/ra_lsp_server/src/conv.rs b/crates/ra_lsp_server/src/conv.rs index 63827aeea..5a911d9d2 100644 --- a/crates/ra_lsp_server/src/conv.rs +++ b/crates/ra_lsp_server/src/conv.rs @@ -235,13 +235,15 @@ impl TryConvWith for SourceChange { None => None, Some(pos) => { let line_index = world.analysis().file_line_index(pos.file_id); - let edits = self + let edit = self .source_file_edits .iter() .find(|it| it.file_id == pos.file_id) - .map(|it| it.edit.as_atoms()) - .unwrap_or(&[]); - let line_col = translate_offset_with_edit(&*line_index, pos.offset, edits); + .map(|it| &it.edit); + let line_col = match edit { + Some(edit) => translate_offset_with_edit(&*line_index, pos.offset, edit), + None => line_index.line_col(pos.offset), + }; let position = Position::new(u64::from(line_col.line), u64::from(line_col.col_utf16)); Some(TextDocumentPositionParams { -- cgit v1.2.3