change to TextEdit to avoid allocation and sort

rename newline to step where applicable
This commit is contained in:
Bernardo 2018-12-25 20:49:18 +01:00
parent 863ed19946
commit e9c186e48a
3 changed files with 51 additions and 57 deletions

View file

@ -1,7 +1,6 @@
use ra_text_edit::AtomTextEdit; use ra_text_edit::{AtomTextEdit, TextEdit};
use ra_syntax::{TextUnit, TextRange}; use ra_syntax::{TextUnit, TextRange};
use crate::{LineIndex, LineCol, line_index::Utf16Char}; use crate::{LineIndex, LineCol, line_index::Utf16Char};
use superslice::Ext;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
enum Step { enum Step {
@ -55,12 +54,12 @@ impl<'a> Iterator for LineIndexStepIter<'a> {
} }
#[derive(Debug)] #[derive(Debug)]
struct OffsetNewlineIter<'a> { struct OffsetStepIter<'a> {
text: &'a str, text: &'a str,
offset: TextUnit, offset: TextUnit,
} }
impl<'a> Iterator for OffsetNewlineIter<'a> { impl<'a> Iterator for OffsetStepIter<'a> {
type Item = Step; type Item = Step;
fn next(&mut self) -> Option<Step> { fn next(&mut self) -> Option<Step> {
let (next, next_offset) = self let (next, next_offset) = self
@ -93,10 +92,10 @@ impl<'a> Iterator for OffsetNewlineIter<'a> {
} }
#[derive(Debug)] #[derive(Debug)]
enum NextNewlines<'a> { enum NextSteps<'a> {
Use, Use,
ReplaceMany(OffsetNewlineIter<'a>), ReplaceMany(OffsetStepIter<'a>),
AddMany(OffsetNewlineIter<'a>), AddMany(OffsetStepIter<'a>),
} }
#[derive(Debug)] #[derive(Debug)]
@ -106,16 +105,16 @@ struct TranslatedEdit<'a> {
diff: i64, diff: i64,
} }
struct Edits<'a, 'b> { struct Edits<'a> {
edits: &'b [&'a AtomTextEdit], edits: &'a [AtomTextEdit],
current: Option<TranslatedEdit<'a>>, current: Option<TranslatedEdit<'a>>,
acc_diff: i64, acc_diff: i64,
} }
impl<'a, 'b> Edits<'a, 'b> { impl<'a> Edits<'a> {
fn new(sorted_edits: &'b [&'a AtomTextEdit]) -> Edits<'a, 'b> { fn from_text_edit(text_edit: &'a TextEdit) -> Edits<'a> {
let mut x = Edits { let mut x = Edits {
edits: sorted_edits, edits: text_edit.as_atoms(),
current: None, current: None,
acc_diff: 0, acc_diff: 0,
}; };
@ -141,9 +140,9 @@ impl<'a, 'b> Edits<'a, 'b> {
} }
} }
fn next_inserted_newlines(&mut self) -> Option<OffsetNewlineIter<'a>> { fn next_inserted_steps(&mut self) -> Option<OffsetStepIter<'a>> {
let cur = self.current.as_ref()?; let cur = self.current.as_ref()?;
let res = Some(OffsetNewlineIter { let res = Some(OffsetStepIter {
offset: cur.delete.start(), offset: cur.delete.start(),
text: &cur.insert, text: &cur.insert,
}); });
@ -151,7 +150,7 @@ impl<'a, 'b> Edits<'a, 'b> {
res res
} }
fn next_step(&mut self, step: &Step) -> NextNewlines { fn next_steps(&mut self, step: &Step) -> NextSteps {
let step_pos = match step { let step_pos = match step {
&Step::Newline(n) => n, &Step::Newline(n) => n,
&Step::Utf16Char(r) => r.end(), &Step::Utf16Char(r) => r.end(),
@ -159,27 +158,27 @@ impl<'a, 'b> Edits<'a, 'b> {
let res = match &mut self.current { let res = match &mut self.current {
Some(edit) => { Some(edit) => {
if step_pos <= edit.delete.start() { if step_pos <= edit.delete.start() {
NextNewlines::Use NextSteps::Use
} else if step_pos <= edit.delete.end() { } else if step_pos <= edit.delete.end() {
let iter = OffsetNewlineIter { let iter = OffsetStepIter {
offset: edit.delete.start(), offset: edit.delete.start(),
text: &edit.insert, text: &edit.insert,
}; };
// empty slice // empty slice to avoid returning steps again
edit.insert = &edit.insert[edit.insert.len()..]; edit.insert = &edit.insert[edit.insert.len()..];
NextNewlines::ReplaceMany(iter) NextSteps::ReplaceMany(iter)
} else { } else {
let iter = OffsetNewlineIter { let iter = OffsetStepIter {
offset: edit.delete.start(), offset: edit.delete.start(),
text: &edit.insert, text: &edit.insert,
}; };
// empty slice // empty slice to avoid returning steps again
edit.insert = &edit.insert[edit.insert.len()..]; edit.insert = &edit.insert[edit.insert.len()..];
self.advance_edit(); self.advance_edit();
NextNewlines::AddMany(iter) NextSteps::AddMany(iter)
} }
} }
None => NextNewlines::Use, None => NextSteps::Use,
}; };
res res
} }
@ -251,16 +250,9 @@ impl RunningLineCol {
pub fn translate_offset_with_edit( pub fn translate_offset_with_edit(
line_index: &LineIndex, line_index: &LineIndex,
offset: TextUnit, offset: TextUnit,
edits: &[AtomTextEdit], text_edit: &TextEdit,
) -> LineCol { ) -> LineCol {
let mut sorted_edits: Vec<&AtomTextEdit> = Vec::with_capacity(edits.len()); let mut state = Edits::from_text_edit(&text_edit);
for edit in edits {
let insert_index =
sorted_edits.upper_bound_by_key(&edit.delete.start(), |x| x.delete.start());
sorted_edits.insert(insert_index, &edit);
}
let mut state = Edits::new(&sorted_edits);
let mut res = RunningLineCol::new(); let mut res = RunningLineCol::new();
@ -291,18 +283,18 @@ pub fn translate_offset_with_edit(
for orig_step in LineIndexStepIter::from(line_index) { for orig_step in LineIndexStepIter::from(line_index) {
loop { loop {
let translated_step = state.translate_step(&orig_step); let translated_step = state.translate_step(&orig_step);
match state.next_step(&translated_step) { match state.next_steps(&translated_step) {
NextNewlines::Use => { NextSteps::Use => {
test_step!(translated_step); test_step!(translated_step);
break; break;
} }
NextNewlines::ReplaceMany(ns) => { NextSteps::ReplaceMany(ns) => {
for n in ns { for n in ns {
test_step!(n); test_step!(n);
} }
break; break;
} }
NextNewlines::AddMany(ns) => { NextSteps::AddMany(ns) => {
for n in ns { for n in ns {
test_step!(n); test_step!(n);
} }
@ -312,7 +304,7 @@ pub fn translate_offset_with_edit(
} }
loop { loop {
match state.next_inserted_newlines() { match state.next_inserted_steps() {
None => break, None => break,
Some(ns) => { Some(ns) => {
for n in ns { for n in ns {
@ -330,26 +322,26 @@ mod test {
use super::*; use super::*;
use proptest::{prelude::*, proptest, proptest_helper}; use proptest::{prelude::*, proptest, proptest_helper};
use crate::line_index; use crate::line_index;
use ra_text_edit::test_utils::{arb_offset, arb_text_with_edits}; use ra_text_edit::test_utils::{arb_offset, arb_text_with_edit};
use ra_text_edit::TextEdit; use ra_text_edit::TextEdit;
#[derive(Debug)] #[derive(Debug)]
struct ArbTextWithOffsetAndEdits { struct ArbTextWithEditAndOffset {
text: String, text: String,
edits: TextEdit, edit: TextEdit,
edited_text: String, edited_text: String,
offset: TextUnit, offset: TextUnit,
} }
fn arb_text_with_edits_and_offset() -> BoxedStrategy<ArbTextWithOffsetAndEdits> { fn arb_text_with_edit_and_offset() -> BoxedStrategy<ArbTextWithEditAndOffset> {
arb_text_with_edits() arb_text_with_edit()
.prop_flat_map(|x| { .prop_flat_map(|x| {
let edited_text = x.edits.apply(&x.text); let edited_text = x.edit.apply(&x.text);
let arb_offset = arb_offset(&edited_text); let arb_offset = arb_offset(&edited_text);
(Just(x), Just(edited_text), arb_offset).prop_map(|(x, edited_text, offset)| { (Just(x), Just(edited_text), arb_offset).prop_map(|(x, edited_text, offset)| {
ArbTextWithOffsetAndEdits { ArbTextWithEditAndOffset {
text: x.text, text: x.text,
edits: x.edits, edit: x.edit,
edited_text, edited_text,
offset, offset,
} }
@ -360,10 +352,10 @@ mod test {
proptest! { proptest! {
#[test] #[test]
fn test_translate_offset_with_edit(x in arb_text_with_edits_and_offset()) { fn test_translate_offset_with_edit(x in arb_text_with_edit_and_offset()) {
let expected = line_index::to_line_col(&x.edited_text, x.offset); let expected = line_index::to_line_col(&x.edited_text, x.offset);
let line_index = LineIndex::new(&x.text); let line_index = LineIndex::new(&x.text);
let actual = translate_offset_with_edit(&line_index, x.offset, x.edits.as_atoms()); let actual = translate_offset_with_edit(&line_index, x.offset, &x.edit);
assert_eq!(actual, expected); assert_eq!(actual, expected);
} }

View file

@ -235,13 +235,15 @@ impl TryConvWith for SourceChange {
None => None, None => None,
Some(pos) => { Some(pos) => {
let line_index = world.analysis().file_line_index(pos.file_id); let line_index = world.analysis().file_line_index(pos.file_id);
let edits = self let edit = self
.source_file_edits .source_file_edits
.iter() .iter()
.find(|it| it.file_id == pos.file_id) .find(|it| it.file_id == pos.file_id)
.map(|it| it.edit.as_atoms()) .map(|it| &it.edit);
.unwrap_or(&[]); let line_col = match edit {
let line_col = translate_offset_with_edit(&*line_index, pos.offset, edits); Some(edit) => translate_offset_with_edit(&*line_index, pos.offset, edit),
None => line_index.line_col(pos.offset),
};
let position = let position =
Position::new(u64::from(line_col.line), u64::from(line_col.col_utf16)); Position::new(u64::from(line_col.line), u64::from(line_col.col_utf16));
Some(TextDocumentPositionParams { Some(TextDocumentPositionParams {

View file

@ -69,17 +69,17 @@ pub fn arb_text_edit(text: &str) -> BoxedStrategy<TextEdit> {
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct ArbTextWithEdits { pub struct ArbTextWithEdit {
pub text: String, pub text: String,
pub edits: TextEdit, pub edit: TextEdit,
} }
pub fn arb_text_with_edits() -> BoxedStrategy<ArbTextWithEdits> { pub fn arb_text_with_edit() -> BoxedStrategy<ArbTextWithEdit> {
let text = arb_text(); let text = arb_text();
text.prop_flat_map(|s| { text.prop_flat_map(|s| {
let edits = arb_text_edit(&s); let edit = arb_text_edit(&s);
(Just(s), edits) (Just(s), edit)
}) })
.prop_map(|(text, edits)| ArbTextWithEdits { text, edits }) .prop_map(|(text, edit)| ArbTextWithEdit { text, edit })
.boxed() .boxed()
} }