1use std::ops::Range;
2
3use ropey::{LineType, Rope, RopeSlice};
4use sum_tree::Bias;
5use tree_sitter::Point;
6
7use crate::input::Position;
8
9pub struct RopeLines<'a> {
11 rope: &'a Rope,
12 row: usize,
13 end_row: usize,
14}
15
16impl<'a> RopeLines<'a> {
17 pub fn new(rope: &'a Rope) -> Self {
19 let end_row = rope.lines_len();
20 Self {
21 row: 0,
22 end_row,
23 rope,
24 }
25 }
26}
27impl<'a> Iterator for RopeLines<'a> {
28 type Item = RopeSlice<'a>;
29
30 #[inline]
31 fn next(&mut self) -> Option<Self::Item> {
32 if self.row >= self.end_row {
33 return None;
34 }
35
36 let line = self.rope.slice_line(self.row);
37 self.row += 1;
38 Some(line)
39 }
40
41 #[inline]
42 fn nth(&mut self, n: usize) -> Option<Self::Item> {
43 self.row = self.row.saturating_add(n);
44 self.next()
45 }
46
47 #[inline]
48 fn size_hint(&self) -> (usize, Option<usize>) {
49 let len = self.end_row - self.row;
50 (len, Some(len))
51 }
52}
53
54impl std::iter::ExactSizeIterator for RopeLines<'_> {}
55impl std::iter::FusedIterator for RopeLines<'_> {}
56
57pub trait RopeExt {
59 fn line_start_offset(&self, row: usize) -> usize;
71
72 fn line_end_offset(&self, row: usize) -> usize;
83
84 fn slice_line(&self, row: usize) -> RopeSlice<'_>;
95
96 fn slice_lines(&self, rows_range: Range<usize>) -> RopeSlice<'_>;
110
111 fn iter_lines(&self) -> RopeLines<'_>;
122
123 fn lines_len(&self) -> usize;
131
132 fn line_len(&self, row: usize) -> usize;
145
146 fn replace(&mut self, range: Range<usize>, new_text: &str);
160
161 fn char_at(&self, offset: usize) -> Option<char>;
166
167 fn position_to_offset(&self, line_col: &Position) -> usize;
171
172 fn offset_to_position(&self, offset: usize) -> Position;
176
177 fn offset_to_point(&self, offset: usize) -> Point;
181
182 fn point_to_offset(&self, point: Point) -> usize;
186
187 fn word_range(&self, offset: usize) -> Option<Range<usize>>;
189
190 fn word_at(&self, offset: usize) -> String;
192
193 fn offset_utf16_to_offset(&self, offset_utf16: usize) -> usize;
197
198 fn offset_to_offset_utf16(&self, offset: usize) -> usize;
202
203 fn clip_offset(&self, offset: usize, bias: Bias) -> usize;
220
221 fn char_index_to_offset(&self, char_index: usize) -> usize;
236
237 fn offset_to_char_index(&self, offset: usize) -> usize;
252}
253
254impl RopeExt for Rope {
255 fn slice_line(&self, row: usize) -> RopeSlice<'_> {
256 let total_lines = self.lines_len();
257 if row >= total_lines {
258 return self.slice(0..0);
259 }
260
261 let line = self.line(row, LineType::LF);
262 if line.len() > 0 {
263 let line_end = line.len() - 1;
264 if line.is_char_boundary(line_end) && line.char(line_end) == '\n' {
265 return line.slice(..line_end);
266 }
267 }
268
269 line
270 }
271
272 fn slice_lines(&self, rows_range: Range<usize>) -> RopeSlice<'_> {
273 let start = self.line_start_offset(rows_range.start);
274 let end = self.line_end_offset(rows_range.end.saturating_sub(1));
275 self.slice(start..end)
276 }
277
278 fn iter_lines(&self) -> RopeLines<'_> {
279 RopeLines::new(&self)
280 }
281
282 fn line_len(&self, row: usize) -> usize {
283 self.slice_line(row).len()
284 }
285
286 fn line_start_offset(&self, row: usize) -> usize {
287 self.point_to_offset(Point::new(row, 0))
288 }
289
290 fn offset_to_point(&self, offset: usize) -> Point {
291 let offset = self.clip_offset(offset, Bias::Left);
292 let row = self.byte_to_line_idx(offset, LineType::LF);
293 let line_start = self.line_to_byte_idx(row, LineType::LF);
294 let column = offset.saturating_sub(line_start);
295 Point::new(row, column)
296 }
297
298 fn point_to_offset(&self, point: Point) -> usize {
299 if point.row >= self.lines_len() {
300 return self.len();
301 }
302
303 let line_start = self.line_to_byte_idx(point.row, LineType::LF);
304 line_start + point.column
305 }
306
307 fn position_to_offset(&self, pos: &Position) -> usize {
308 let line = self.slice_line(pos.line as usize);
309 self.line_start_offset(pos.line as usize)
310 + line
311 .chars()
312 .take(pos.character as usize)
313 .map(|c| c.len_utf8())
314 .sum::<usize>()
315 }
316
317 fn offset_to_position(&self, offset: usize) -> Position {
318 let point = self.offset_to_point(offset);
319 let line = self.slice_line(point.row);
320 let offset = line.utf16_to_byte_idx(line.byte_to_utf16_idx(point.column));
321 let character = line.slice(..offset).chars().count();
322 Position::new(point.row as u32, character as u32)
323 }
324
325 fn line_end_offset(&self, row: usize) -> usize {
326 if row > self.lines_len() {
327 return self.len();
328 }
329
330 self.line_start_offset(row) + self.line_len(row)
331 }
332
333 fn lines_len(&self) -> usize {
334 self.len_lines(LineType::LF)
335 }
336
337 fn char_at(&self, offset: usize) -> Option<char> {
338 if offset > self.len() {
339 return None;
340 }
341
342 self.get_char(offset).ok()
343 }
344
345 fn word_range(&self, offset: usize) -> Option<Range<usize>> {
346 if offset >= self.len() {
347 return None;
348 }
349
350 let mut left = String::new();
351 let offset = self.clip_offset(offset, Bias::Left);
352 for c in self.chars_at(offset).reversed() {
353 if c.is_alphanumeric() || c == '_' {
354 left.insert(0, c);
355 } else {
356 break;
357 }
358 }
359 let start = offset.saturating_sub(left.len());
360
361 let right = self
362 .chars_at(offset)
363 .take_while(|c| c.is_alphanumeric() || *c == '_')
364 .collect::<String>();
365
366 let end = offset + right.len();
367
368 if start == end {
369 None
370 } else {
371 Some(start..end)
372 }
373 }
374
375 fn word_at(&self, offset: usize) -> String {
376 if let Some(range) = self.word_range(offset) {
377 self.slice(range).to_string()
378 } else {
379 String::new()
380 }
381 }
382
383 #[inline]
384 fn offset_utf16_to_offset(&self, offset_utf16: usize) -> usize {
385 if offset_utf16 > self.len_utf16() {
386 return self.len();
387 }
388
389 self.utf16_to_byte_idx(offset_utf16)
390 }
391
392 #[inline]
393 fn offset_to_offset_utf16(&self, offset: usize) -> usize {
394 if offset > self.len() {
395 return self.len_utf16();
396 }
397
398 self.byte_to_utf16_idx(offset)
399 }
400
401 fn replace(&mut self, range: Range<usize>, new_text: &str) {
402 let range =
403 self.clip_offset(range.start, Bias::Left)..self.clip_offset(range.end, Bias::Right);
404 self.remove(range.clone());
405 self.insert(range.start, new_text);
406 }
407
408 fn clip_offset(&self, offset: usize, bias: Bias) -> usize {
409 if offset > self.len() {
410 return self.len();
411 }
412
413 if self.is_char_boundary(offset) {
414 return offset;
415 }
416
417 if bias == Bias::Left {
418 self.floor_char_boundary(offset)
419 } else {
420 self.ceil_char_boundary(offset)
421 }
422 }
423
424 fn char_index_to_offset(&self, char_offset: usize) -> usize {
425 self.chars().take(char_offset).map(|c| c.len_utf8()).sum()
426 }
427
428 fn offset_to_char_index(&self, offset: usize) -> usize {
429 let offset = self.clip_offset(offset, Bias::Right);
430 self.slice(..offset).chars().count()
431 }
432}
433
434#[cfg(test)]
435mod tests {
436 use ropey::Rope;
437 use sum_tree::Bias;
438 use tree_sitter::Point;
439
440 use crate::{input::Position, RopeExt};
441
442 #[test]
443 fn test_slice_line() {
444 let rope = Rope::from("Hello\nWorld\r\nThis is a test 中文\nRope");
445 assert_eq!(rope.slice_line(0).to_string(), "Hello");
446 assert_eq!(rope.slice_line(1).to_string(), "World\r");
447 assert_eq!(rope.slice_line(2).to_string(), "This is a test 中文");
448 assert_eq!(rope.slice_line(3).to_string(), "Rope");
449
450 assert_eq!(rope.slice_line(6).to_string(), "");
452
453 let rope = Rope::from("Hello\r");
455 assert_eq!(rope.slice_line(0).to_string(), "Hello\r");
456 assert_eq!(rope.slice_line(1).to_string(), "");
457 }
458
459 #[test]
460 fn test_lines_len() {
461 let rope = Rope::from("Hello\nWorld\r\nThis is a test 中文\nRope");
462 assert_eq!(rope.lines_len(), 4);
463 let rope = Rope::from("");
464 assert_eq!(rope.lines_len(), 1);
465 let rope = Rope::from("Single line");
466 assert_eq!(rope.lines_len(), 1);
467
468 let rope = Rope::from("Hello\r");
470 assert_eq!(rope.lines_len(), 1);
471 }
472
473 #[test]
474 fn test_lines() {
475 let rope = Rope::from("Hello\nWorld\r\nThis is a test 中文\nRope\r");
476 let lines: Vec<_> = rope.iter_lines().map(|r| r.to_string()).collect();
477 assert_eq!(
478 lines,
479 vec!["Hello", "World\r", "This is a test 中文", "Rope\r"]
480 );
481 }
482
483 #[test]
484 fn test_eq() {
485 let rope = Rope::from("Hello\nWorld\r\nThis is a test 中文\nRope");
486 assert!(rope.eq(&Rope::from("Hello\nWorld\r\nThis is a test 中文\nRope")));
487 assert!(!rope.eq(&Rope::from("Hello\nWorld")));
488
489 let rope1 = rope.clone();
490 assert!(rope.eq(&rope1));
491 }
492
493 #[test]
494 fn test_iter_lines() {
495 let rope = Rope::from("Hello\nWorld\r\nThis is a test 中文\nRope");
496 let lines: Vec<_> = rope
497 .iter_lines()
498 .skip(1)
499 .take(2)
500 .map(|r| r.to_string())
501 .collect();
502 assert_eq!(lines, vec!["World\r", "This is a test 中文"]);
503 }
504
505 #[test]
506 fn test_line_start_end_offset() {
507 let rope = Rope::from("Hello\nWorld\r\nThis is a test 中文\nRope");
508 assert_eq!(rope.line_start_offset(0), 0);
509 assert_eq!(rope.line_end_offset(0), 5);
510
511 assert_eq!(rope.line_start_offset(1), 6);
512 assert_eq!(rope.line_end_offset(1), 12);
513
514 assert_eq!(rope.line_start_offset(2), 13);
515 assert_eq!(rope.line_end_offset(2), 34);
516
517 assert_eq!(rope.line_start_offset(3), 35);
518 assert_eq!(rope.line_end_offset(3), 39);
519
520 assert_eq!(rope.line_start_offset(4), 39);
521 assert_eq!(rope.line_end_offset(4), 39);
522 }
523
524 #[test]
525 fn test_line_column() {
526 let rope = Rope::from("a 中文🎉 test\nRope");
527 assert_eq!(rope.position_to_offset(&Position::new(0, 3)), "a 中".len());
528 assert_eq!(
529 rope.position_to_offset(&Position::new(0, 5)),
530 "a 中文🎉".len()
531 );
532 assert_eq!(
533 rope.position_to_offset(&Position::new(1, 1)),
534 "a 中文🎉 test\nR".len()
535 );
536
537 assert_eq!(
538 rope.offset_to_position("a 中文🎉 test\nR".len()),
539 Position::new(1, 1)
540 );
541 assert_eq!(
542 rope.offset_to_position("a 中文🎉".len()),
543 Position::new(0, 5)
544 );
545 }
546
547 #[test]
548 fn test_offset_to_point() {
549 let rope = Rope::from("a 中文🎉 test\nRope");
550 assert_eq!(rope.offset_to_point(0), Point::new(0, 0));
551 assert_eq!(rope.offset_to_point(1), Point::new(0, 1));
552 assert_eq!(rope.offset_to_point("a 中".len()), Point::new(0, 5));
553 assert_eq!(rope.offset_to_point("a 中文🎉".len()), Point::new(0, 12));
554 assert_eq!(
555 rope.offset_to_point("a 中文🎉 test\nR".len()),
556 Point::new(1, 1)
557 );
558 }
559
560 #[test]
561 fn test_point_to_offset() {
562 let rope = Rope::from("a 中文🎉 test\nRope");
563 assert_eq!(rope.point_to_offset(Point::new(0, 0)), 0);
564 assert_eq!(rope.point_to_offset(Point::new(0, 1)), 1);
565 assert_eq!(rope.point_to_offset(Point::new(0, 5)), "a 中".len());
566 assert_eq!(rope.point_to_offset(Point::new(0, 12)), "a 中文🎉".len());
567 assert_eq!(
568 rope.point_to_offset(Point::new(1, 1)),
569 "a 中文🎉 test\nR".len()
570 );
571 }
572
573 #[test]
574 fn test_char_at() {
575 let rope = Rope::from("Hello\nWorld\r\nThis is a test 中文🎉\nRope");
576 assert_eq!(rope.char_at(0), Some('H'));
577 assert_eq!(rope.char_at(5), Some('\n'));
578 assert_eq!(rope.char_at(13), Some('T'));
579 assert_eq!(rope.char_at(28), Some('中'));
580 assert_eq!(rope.char_at(34), Some('🎉'));
581 assert_eq!(rope.char_at(38), Some('\n'));
582 assert_eq!(rope.char_at(50), None);
583 }
584
585 #[test]
586 fn test_word_at() {
587 let rope = Rope::from("Hello\nWorld\r\nThis is a test 中文 世界\nRope");
588 assert_eq!(rope.word_at(0), "Hello");
589 assert_eq!(rope.word_range(0), Some(0..5));
590 assert_eq!(rope.word_at(8), "World");
591 assert_eq!(rope.word_range(8), Some(6..11));
592 assert_eq!(rope.word_at(12), "");
593 assert_eq!(rope.word_range(12), None);
594 assert_eq!(rope.word_at(13), "This");
595 assert_eq!(rope.word_range(13), Some(13..17));
596 assert_eq!(rope.word_at(31), "中文");
597 assert_eq!(rope.word_range(31), Some(28..34));
598 assert_eq!(rope.word_at(38), "世界");
599 assert_eq!(rope.word_range(38), Some(35..41));
600 assert_eq!(rope.word_at(44), "Rope");
601 assert_eq!(rope.word_range(44), Some(42..46));
602 assert_eq!(rope.word_at(45), "Rope");
603 }
604
605 #[test]
606 fn test_offset_utf16_conversion() {
607 let rope = Rope::from("hello 中文🎉 test\nRope");
608 assert_eq!(rope.offset_to_offset_utf16("hello".len()), 5);
609 assert_eq!(rope.offset_to_offset_utf16("hello 中".len()), 7);
610 assert_eq!(rope.offset_to_offset_utf16("hello 中文".len()), 8);
611 assert_eq!(rope.offset_to_offset_utf16("hello 中文🎉".len()), 10);
612 assert_eq!(rope.offset_to_offset_utf16(100), 20);
613
614 assert_eq!(rope.offset_utf16_to_offset(5), "hello".len());
615 assert_eq!(rope.offset_utf16_to_offset(7), "hello 中".len());
616 assert_eq!(rope.offset_utf16_to_offset(8), "hello 中文".len());
617 assert_eq!(rope.offset_utf16_to_offset(10), "hello 中文🎉".len());
618 assert_eq!(rope.offset_utf16_to_offset(100), rope.len());
619 }
620
621 #[test]
622 fn test_replace() {
623 let mut rope = Rope::from("Hello\nWorld\r\nThis is a test 中文\nRope");
624 rope.replace(6..11, "Universe");
625 assert_eq!(
626 rope.to_string(),
627 "Hello\nUniverse\r\nThis is a test 中文\nRope"
628 );
629
630 rope.replace(0..5, "Hi");
631 assert_eq!(
632 rope.to_string(),
633 "Hi\nUniverse\r\nThis is a test 中文\nRope"
634 );
635
636 rope.replace(rope.len() - 4..rope.len(), "String");
637 assert_eq!(
638 rope.to_string(),
639 "Hi\nUniverse\r\nThis is a test 中文\nString"
640 );
641
642 let mut rope = Rope::from("中文");
644 rope.replace(0..1, "New");
645 assert_eq!(rope.to_string(), "New文");
647 let mut rope = Rope::from("中文");
648 rope.replace(0..2, "New");
649 assert_eq!(rope.to_string(), "New文");
650 let mut rope = Rope::from("中文");
651 rope.replace(0..3, "New");
652 assert_eq!(rope.to_string(), "New文");
653 let mut rope = Rope::from("中文");
655 rope.replace(1..4, "New");
656 assert_eq!(rope.to_string(), "New");
657 }
658
659 #[test]
660 fn test_clip_offset() {
661 let rope = Rope::from("Hello 中文🎉 test\nRope");
662 assert_eq!(rope.clip_offset(5, Bias::Left), 5);
664 assert_eq!(rope.clip_offset(7, Bias::Left), 6);
665 assert_eq!(rope.clip_offset(7, Bias::Right), 9);
666 assert_eq!(rope.clip_offset(9, Bias::Left), 9);
667
668 assert_eq!(rope.clip_offset(13, Bias::Left), 12);
670 assert_eq!(rope.clip_offset(13, Bias::Right), 16);
671 assert_eq!(rope.clip_offset(16, Bias::Left), 16);
672
673 assert_eq!(rope.clip_offset(5, Bias::Left), 5);
675 assert_eq!(rope.clip_offset(5, Bias::Right), 5);
676
677 assert_eq!(rope.clip_offset(26, Bias::Left), 26);
679 assert_eq!(rope.clip_offset(100, Bias::Left), 26);
680 }
681
682 #[test]
683 fn test_char_index_to_offset() {
684 let rope = Rope::from("a 中文🎉 test\nRope");
685 assert_eq!(rope.char_index_to_offset(0), 0);
686 assert_eq!(rope.char_index_to_offset(1), 1);
687 assert_eq!(rope.char_index_to_offset(3), "a 中".len());
688 assert_eq!(rope.char_index_to_offset(5), "a 中文🎉".len());
689 assert_eq!(rope.char_index_to_offset(6), "a 中文🎉 ".len());
690
691 assert_eq!(rope.offset_to_char_index(0), 0);
692 assert_eq!(rope.offset_to_char_index(1), 1);
693 assert_eq!(rope.offset_to_char_index(3), 3);
694 assert_eq!(rope.offset_to_char_index(4), 3);
695 assert_eq!(rope.offset_to_char_index(5), 3);
696 assert_eq!(rope.offset_to_char_index(6), 4);
697 assert_eq!(rope.offset_to_char_index(10), 5);
698 }
699}