fix arbitrary offset generation, col translation working

This commit is contained in:
Bernardo 2018-12-22 20:52:43 +01:00
parent 5c8525ce4a
commit dc2afae991
2 changed files with 92 additions and 39 deletions

View file

@ -62,6 +62,12 @@ impl LineIndex {
curr_col += char_len; curr_col += char_len;
} }
// Save any utf-16 characters seen in the last line
if utf16_chars.len() > 0 {
utf16_lines.insert(line, utf16_chars);
}
LineIndex { LineIndex {
newlines, newlines,
utf16_lines, utf16_lines,
@ -122,6 +128,28 @@ impl LineIndex {
} }
} }
// for bench and test
pub fn to_line_col(text: &str, offset: TextUnit) -> LineCol {
let mut res = LineCol {
line: 0,
col_utf16: 0,
};
for (i, c) in text.char_indices() {
if i + c.len_utf8() > offset.to_usize() {
// if it's an invalid offset, inside a multibyte char
// return as if it was at the start of the char
break;
}
if c == '\n' {
res.line += 1;
res.col_utf16 = 0;
} else {
res.col_utf16 += 1;
}
}
res
}
#[test] #[test]
fn test_line_index() { fn test_line_index() {
let text = "hello\nworld"; let text = "hello\nworld";

View file

@ -1,6 +1,6 @@
use ra_text_edit::AtomTextEdit; use ra_text_edit::AtomTextEdit;
use ra_syntax::{TextUnit, TextRange}; use ra_syntax::{TextUnit, TextRange};
use crate::{LineIndex, LineCol, line_index::Utf16Char}; use crate::{LineIndex, LineCol, line_index::Utf16Char, line_index};
use superslice::Ext; use superslice::Ext;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -154,7 +154,7 @@ impl<'a, 'b> Edits<'a, 'b> {
fn next_step(&mut self, step: &Step) -> NextNewlines { fn next_step(&mut self, step: &Step) -> NextNewlines {
let step_pos = match step { let step_pos = match step {
&Step::Newline(n) => n, &Step::Newline(n) => n,
&Step::Utf16Char(r) => r.start(), &Step::Utf16Char(r) => r.end(),
}; };
let res = match &mut self.current { let res = match &mut self.current {
Some(edit) => { Some(edit) => {
@ -214,6 +214,40 @@ impl<'a, 'b> Edits<'a, 'b> {
} }
} }
#[derive(Debug)]
struct RunningLineCol {
line: u32,
last_newline: TextUnit,
col_adjust: TextUnit,
}
impl RunningLineCol {
fn new() -> RunningLineCol {
RunningLineCol {
line: 0,
last_newline: TextUnit::from(0),
col_adjust: TextUnit::from(0),
}
}
fn to_line_col(&self, offset: TextUnit) -> LineCol {
LineCol {
line: self.line,
col_utf16: ((offset - self.last_newline) - self.col_adjust).into(),
}
}
fn add_line(&mut self, newline: TextUnit) {
self.line += 1;
self.last_newline = newline;
self.col_adjust = TextUnit::from(0);
}
fn adjust_col(&mut self, range: &TextRange) {
self.col_adjust += range.len() - TextUnit::from(1);
}
}
pub fn translate_offset_with_edit( pub fn translate_offset_with_edit(
line_index: &LineIndex, line_index: &LineIndex,
offset: TextUnit, offset: TextUnit,
@ -228,49 +262,35 @@ pub fn translate_offset_with_edit(
let mut state = Edits::new(&sorted_edits); let mut state = Edits::new(&sorted_edits);
let mut pos: LineCol = LineCol { let mut res = RunningLineCol::new();
line: 0,
col_utf16: 0,
};
let mut last_newline: TextUnit = TextUnit::from(0);
let mut col_adjust: TextUnit = TextUnit::from(0);
macro_rules! test_step { macro_rules! test_step {
($x:ident) => { ($x:ident) => {
match &$x { match &$x {
Step::Newline(n) => { Step::Newline(n) => {
if offset < *n { if offset < *n {
return_pos!(); return res.to_line_col(offset);
} else if offset == *n { } else if offset == *n {
pos.line += 1; res.add_line(*n);
pos.col_utf16 = 0; return res.to_line_col(offset);
return pos;
} else { } else {
pos.line += 1; res.add_line(*n);
pos.col_utf16 = 0;
last_newline = *n;
col_adjust = TextUnit::from(0);
} }
} }
Step::Utf16Char(x) => { Step::Utf16Char(x) => {
if offset < x.end() { if offset < x.end() {
return_pos!(); // if the offset is inside a multibyte char it's invalid
// clamp it to the start of the char
let clamp = offset.min(x.start());
return res.to_line_col(clamp);
} else { } else {
col_adjust += x.len() - TextUnit::from(1); res.adjust_col(x);
} }
} }
} }
}; };
} }
macro_rules! return_pos {
() => {
pos.col_utf16 = ((offset - last_newline) - col_adjust).into();
return pos;
};
}
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);
@ -305,7 +325,7 @@ pub fn translate_offset_with_edit(
} }
} }
return_pos!(); res.to_line_col(offset)
} }
// for bench // for bench
@ -315,8 +335,7 @@ pub fn translate_after_edit(
edits: Vec<AtomTextEdit>, edits: Vec<AtomTextEdit>,
) -> LineCol { ) -> LineCol {
let text = edit_text(pre_edit_text, edits); let text = edit_text(pre_edit_text, edits);
let line_index = LineIndex::new(&text); line_index::to_line_col(&text, offset)
line_index.line_col(offset)
} }
fn edit_text(pre_edit_text: &str, mut edits: Vec<AtomTextEdit>) -> String { fn edit_text(pre_edit_text: &str, mut edits: Vec<AtomTextEdit>) -> String {
@ -343,6 +362,7 @@ mod test {
#[derive(Debug)] #[derive(Debug)]
struct ArbTextWithOffsetAndEdits { struct ArbTextWithOffsetAndEdits {
text: String, text: String,
edited_text: String,
offset: TextUnit, offset: TextUnit,
edits: Vec<AtomTextEdit>, edits: Vec<AtomTextEdit>,
} }
@ -350,13 +370,18 @@ mod test {
fn arb_text_with_offset_and_edits() -> BoxedStrategy<ArbTextWithOffsetAndEdits> { fn arb_text_with_offset_and_edits() -> BoxedStrategy<ArbTextWithOffsetAndEdits> {
arb_text() arb_text()
.prop_flat_map(|text| { .prop_flat_map(|text| {
(arb_offset(&text), arb_edits(&text), Just(text)).prop_map( (arb_edits(&text), Just(text)).prop_flat_map(|(edits, text)| {
|(offset, edits, text)| ArbTextWithOffsetAndEdits { let edited_text = edit_text(&text, edits.clone());
text, let arb_offset = arb_offset(&edited_text);
offset, (Just(text), Just(edited_text), Just(edits), arb_offset).prop_map(
edits, |(text, edited_text, edits, offset)| ArbTextWithOffsetAndEdits {
}, text,
) edits,
edited_text,
offset,
},
)
})
}) })
.boxed() .boxed()
} }
@ -364,11 +389,11 @@ mod test {
proptest! { proptest! {
#[test] #[test]
fn test_translate_offset_with_edit(x in arb_text_with_offset_and_edits()) { fn test_translate_offset_with_edit(x in arb_text_with_offset_and_edits()) {
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 expected = translate_after_edit(&x.text, x.offset, x.edits.clone());
let actual = translate_offset_with_edit(&line_index, x.offset, &x.edits); let actual = translate_offset_with_edit(&line_index, x.offset, &x.edits);
// assert_eq!(actual, expected);
assert_eq!(actual.line, expected.line); assert_eq!(actual, expected);
} }
} }
} }