mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-25 20:43:21 +00:00
change to TextEdit
to avoid allocation and sort
rename newline to step where applicable
This commit is contained in:
parent
863ed19946
commit
e9c186e48a
3 changed files with 51 additions and 57 deletions
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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()
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue