From dc2afae991892719b97b0e4b40d8483b43b08680 Mon Sep 17 00:00:00 2001 From: Bernardo Date: Sat, 22 Dec 2018 20:52:43 +0100 Subject: fix arbitrary offset generation, col translation working --- crates/ra_editor/src/line_index.rs | 28 +++++++++ crates/ra_editor/src/line_index_utils.rs | 103 +++++++++++++++++++------------ 2 files changed, 92 insertions(+), 39 deletions(-) diff --git a/crates/ra_editor/src/line_index.rs b/crates/ra_editor/src/line_index.rs index 6dbabd97e..b01760313 100644 --- a/crates/ra_editor/src/line_index.rs +++ b/crates/ra_editor/src/line_index.rs @@ -62,6 +62,12 @@ impl LineIndex { 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 { newlines, 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] fn test_line_index() { let text = "hello\nworld"; diff --git a/crates/ra_editor/src/line_index_utils.rs b/crates/ra_editor/src/line_index_utils.rs index 382f3ac72..913ca5b15 100644 --- a/crates/ra_editor/src/line_index_utils.rs +++ b/crates/ra_editor/src/line_index_utils.rs @@ -1,6 +1,6 @@ use ra_text_edit::AtomTextEdit; use ra_syntax::{TextUnit, TextRange}; -use crate::{LineIndex, LineCol, line_index::Utf16Char}; +use crate::{LineIndex, LineCol, line_index::Utf16Char, line_index}; use superslice::Ext; #[derive(Debug, Clone)] @@ -154,7 +154,7 @@ impl<'a, 'b> Edits<'a, 'b> { fn next_step(&mut self, step: &Step) -> NextNewlines { let step_pos = match step { &Step::Newline(n) => n, - &Step::Utf16Char(r) => r.start(), + &Step::Utf16Char(r) => r.end(), }; let res = match &mut self.current { 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( line_index: &LineIndex, offset: TextUnit, @@ -228,49 +262,35 @@ pub fn translate_offset_with_edit( let mut state = Edits::new(&sorted_edits); - let mut pos: LineCol = LineCol { - line: 0, - col_utf16: 0, - }; - - let mut last_newline: TextUnit = TextUnit::from(0); - let mut col_adjust: TextUnit = TextUnit::from(0); + let mut res = RunningLineCol::new(); macro_rules! test_step { ($x:ident) => { match &$x { Step::Newline(n) => { if offset < *n { - return_pos!(); + return res.to_line_col(offset); } else if offset == *n { - pos.line += 1; - pos.col_utf16 = 0; - return pos; + res.add_line(*n); + return res.to_line_col(offset); } else { - pos.line += 1; - pos.col_utf16 = 0; - last_newline = *n; - col_adjust = TextUnit::from(0); + res.add_line(*n); } } Step::Utf16Char(x) => { 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 { - 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) { loop { 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 @@ -315,8 +335,7 @@ pub fn translate_after_edit( edits: Vec, ) -> LineCol { let text = edit_text(pre_edit_text, edits); - let line_index = LineIndex::new(&text); - line_index.line_col(offset) + line_index::to_line_col(&text, offset) } fn edit_text(pre_edit_text: &str, mut edits: Vec) -> String { @@ -343,6 +362,7 @@ mod test { #[derive(Debug)] struct ArbTextWithOffsetAndEdits { text: String, + edited_text: String, offset: TextUnit, edits: Vec, } @@ -350,13 +370,18 @@ mod test { 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, - }, - ) + (arb_edits(&text), Just(text)).prop_flat_map(|(edits, text)| { + let edited_text = edit_text(&text, edits.clone()); + let arb_offset = arb_offset(&edited_text); + (Just(text), Just(edited_text), Just(edits), arb_offset).prop_map( + |(text, edited_text, edits, offset)| ArbTextWithOffsetAndEdits { + text, + edits, + edited_text, + offset, + }, + ) + }) }) .boxed() } @@ -364,11 +389,11 @@ mod test { proptest! { #[test] 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 expected = translate_after_edit(&x.text, x.offset, x.edits.clone()); 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); } } } -- cgit v1.2.3