aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBernardo <[email protected]>2018-12-22 19:52:43 +0000
committerBernardo <[email protected]>2018-12-25 19:03:14 +0000
commitdc2afae991892719b97b0e4b40d8483b43b08680 (patch)
tree67509ccaf4599caa102a494521e9597e1453f30f
parent5c8525ce4ae8bb969f2ac263bf14adad1c835c03 (diff)
fix arbitrary offset generation, col translation working
-rw-r--r--crates/ra_editor/src/line_index.rs28
-rw-r--r--crates/ra_editor/src/line_index_utils.rs103
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 {
62 62
63 curr_col += char_len; 63 curr_col += char_len;
64 } 64 }
65
66 // Save any utf-16 characters seen in the last line
67 if utf16_chars.len() > 0 {
68 utf16_lines.insert(line, utf16_chars);
69 }
70
65 LineIndex { 71 LineIndex {
66 newlines, 72 newlines,
67 utf16_lines, 73 utf16_lines,
@@ -122,6 +128,28 @@ impl LineIndex {
122 } 128 }
123} 129}
124 130
131// for bench and test
132pub fn to_line_col(text: &str, offset: TextUnit) -> LineCol {
133 let mut res = LineCol {
134 line: 0,
135 col_utf16: 0,
136 };
137 for (i, c) in text.char_indices() {
138 if i + c.len_utf8() > offset.to_usize() {
139 // if it's an invalid offset, inside a multibyte char
140 // return as if it was at the start of the char
141 break;
142 }
143 if c == '\n' {
144 res.line += 1;
145 res.col_utf16 = 0;
146 } else {
147 res.col_utf16 += 1;
148 }
149 }
150 res
151}
152
125#[test] 153#[test]
126fn test_line_index() { 154fn test_line_index() {
127 let text = "hello\nworld"; 155 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 @@
1use ra_text_edit::AtomTextEdit; 1use ra_text_edit::AtomTextEdit;
2use ra_syntax::{TextUnit, TextRange}; 2use ra_syntax::{TextUnit, TextRange};
3use crate::{LineIndex, LineCol, line_index::Utf16Char}; 3use crate::{LineIndex, LineCol, line_index::Utf16Char, line_index};
4use superslice::Ext; 4use superslice::Ext;
5 5
6#[derive(Debug, Clone)] 6#[derive(Debug, Clone)]
@@ -154,7 +154,7 @@ impl<'a, 'b> Edits<'a, 'b> {
154 fn next_step(&mut self, step: &Step) -> NextNewlines { 154 fn next_step(&mut self, step: &Step) -> NextNewlines {
155 let step_pos = match step { 155 let step_pos = match step {
156 &Step::Newline(n) => n, 156 &Step::Newline(n) => n,
157 &Step::Utf16Char(r) => r.start(), 157 &Step::Utf16Char(r) => r.end(),
158 }; 158 };
159 let res = match &mut self.current { 159 let res = match &mut self.current {
160 Some(edit) => { 160 Some(edit) => {
@@ -214,6 +214,40 @@ impl<'a, 'b> Edits<'a, 'b> {
214 } 214 }
215} 215}
216 216
217#[derive(Debug)]
218struct RunningLineCol {
219 line: u32,
220 last_newline: TextUnit,
221 col_adjust: TextUnit,
222}
223
224impl RunningLineCol {
225 fn new() -> RunningLineCol {
226 RunningLineCol {
227 line: 0,
228 last_newline: TextUnit::from(0),
229 col_adjust: TextUnit::from(0),
230 }
231 }
232
233 fn to_line_col(&self, offset: TextUnit) -> LineCol {
234 LineCol {
235 line: self.line,
236 col_utf16: ((offset - self.last_newline) - self.col_adjust).into(),
237 }
238 }
239
240 fn add_line(&mut self, newline: TextUnit) {
241 self.line += 1;
242 self.last_newline = newline;
243 self.col_adjust = TextUnit::from(0);
244 }
245
246 fn adjust_col(&mut self, range: &TextRange) {
247 self.col_adjust += range.len() - TextUnit::from(1);
248 }
249}
250
217pub fn translate_offset_with_edit( 251pub fn translate_offset_with_edit(
218 line_index: &LineIndex, 252 line_index: &LineIndex,
219 offset: TextUnit, 253 offset: TextUnit,
@@ -228,49 +262,35 @@ pub fn translate_offset_with_edit(
228 262
229 let mut state = Edits::new(&sorted_edits); 263 let mut state = Edits::new(&sorted_edits);
230 264
231 let mut pos: LineCol = LineCol { 265 let mut res = RunningLineCol::new();
232 line: 0,
233 col_utf16: 0,
234 };
235
236 let mut last_newline: TextUnit = TextUnit::from(0);
237 let mut col_adjust: TextUnit = TextUnit::from(0);
238 266
239 macro_rules! test_step { 267 macro_rules! test_step {
240 ($x:ident) => { 268 ($x:ident) => {
241 match &$x { 269 match &$x {
242 Step::Newline(n) => { 270 Step::Newline(n) => {
243 if offset < *n { 271 if offset < *n {
244 return_pos!(); 272 return res.to_line_col(offset);
245 } else if offset == *n { 273 } else if offset == *n {
246 pos.line += 1; 274 res.add_line(*n);
247 pos.col_utf16 = 0; 275 return res.to_line_col(offset);
248 return pos;
249 } else { 276 } else {
250 pos.line += 1; 277 res.add_line(*n);
251 pos.col_utf16 = 0;
252 last_newline = *n;
253 col_adjust = TextUnit::from(0);
254 } 278 }
255 } 279 }
256 Step::Utf16Char(x) => { 280 Step::Utf16Char(x) => {
257 if offset < x.end() { 281 if offset < x.end() {
258 return_pos!(); 282 // if the offset is inside a multibyte char it's invalid
283 // clamp it to the start of the char
284 let clamp = offset.min(x.start());
285 return res.to_line_col(clamp);
259 } else { 286 } else {
260 col_adjust += x.len() - TextUnit::from(1); 287 res.adjust_col(x);
261 } 288 }
262 } 289 }
263 } 290 }
264 }; 291 };
265 } 292 }
266 293
267 macro_rules! return_pos {
268 () => {
269 pos.col_utf16 = ((offset - last_newline) - col_adjust).into();
270 return pos;
271 };
272 }
273
274 for orig_step in LineIndexStepIter::from(line_index) { 294 for orig_step in LineIndexStepIter::from(line_index) {
275 loop { 295 loop {
276 let translated_step = state.translate_step(&orig_step); 296 let translated_step = state.translate_step(&orig_step);
@@ -305,7 +325,7 @@ pub fn translate_offset_with_edit(
305 } 325 }
306 } 326 }
307 327
308 return_pos!(); 328 res.to_line_col(offset)
309} 329}
310 330
311// for bench 331// for bench
@@ -315,8 +335,7 @@ pub fn translate_after_edit(
315 edits: Vec<AtomTextEdit>, 335 edits: Vec<AtomTextEdit>,
316) -> LineCol { 336) -> LineCol {
317 let text = edit_text(pre_edit_text, edits); 337 let text = edit_text(pre_edit_text, edits);
318 let line_index = LineIndex::new(&text); 338 line_index::to_line_col(&text, offset)
319 line_index.line_col(offset)
320} 339}
321 340
322fn edit_text(pre_edit_text: &str, mut edits: Vec<AtomTextEdit>) -> String { 341fn edit_text(pre_edit_text: &str, mut edits: Vec<AtomTextEdit>) -> String {
@@ -343,6 +362,7 @@ mod test {
343 #[derive(Debug)] 362 #[derive(Debug)]
344 struct ArbTextWithOffsetAndEdits { 363 struct ArbTextWithOffsetAndEdits {
345 text: String, 364 text: String,
365 edited_text: String,
346 offset: TextUnit, 366 offset: TextUnit,
347 edits: Vec<AtomTextEdit>, 367 edits: Vec<AtomTextEdit>,
348 } 368 }
@@ -350,13 +370,18 @@ mod test {
350 fn arb_text_with_offset_and_edits() -> BoxedStrategy<ArbTextWithOffsetAndEdits> { 370 fn arb_text_with_offset_and_edits() -> BoxedStrategy<ArbTextWithOffsetAndEdits> {
351 arb_text() 371 arb_text()
352 .prop_flat_map(|text| { 372 .prop_flat_map(|text| {
353 (arb_offset(&text), arb_edits(&text), Just(text)).prop_map( 373 (arb_edits(&text), Just(text)).prop_flat_map(|(edits, text)| {
354 |(offset, edits, text)| ArbTextWithOffsetAndEdits { 374 let edited_text = edit_text(&text, edits.clone());
355 text, 375 let arb_offset = arb_offset(&edited_text);
356 offset, 376 (Just(text), Just(edited_text), Just(edits), arb_offset).prop_map(
357 edits, 377 |(text, edited_text, edits, offset)| ArbTextWithOffsetAndEdits {
358 }, 378 text,
359 ) 379 edits,
380 edited_text,
381 offset,
382 },
383 )
384 })
360 }) 385 })
361 .boxed() 386 .boxed()
362 } 387 }
@@ -364,11 +389,11 @@ mod test {
364 proptest! { 389 proptest! {
365 #[test] 390 #[test]
366 fn test_translate_offset_with_edit(x in arb_text_with_offset_and_edits()) { 391 fn test_translate_offset_with_edit(x in arb_text_with_offset_and_edits()) {
392 let expected = line_index::to_line_col(&x.edited_text, x.offset);
367 let line_index = LineIndex::new(&x.text); 393 let line_index = LineIndex::new(&x.text);
368 let expected = translate_after_edit(&x.text, x.offset, x.edits.clone());
369 let actual = translate_offset_with_edit(&line_index, x.offset, &x.edits); 394 let actual = translate_offset_with_edit(&line_index, x.offset, &x.edits);
370 // assert_eq!(actual, expected); 395
371 assert_eq!(actual.line, expected.line); 396 assert_eq!(actual, expected);
372 } 397 }
373 } 398 }
374} 399}