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
222impl RopeExt for Rope {
223 fn slice_line(&self, row: usize) -> RopeSlice<'_> {
224 let total_lines = self.lines_len();
225 if row >= total_lines {
226 return self.slice(0..0);
227 }
228
229 let line = self.line(row, LineType::LF);
230 if line.len() > 0 {
231 let line_end = line.len() - 1;
232 if line.is_char_boundary(line_end) && line.char(line_end) == '\n' {
233 return line.slice(..line_end);
234 }
235 }
236
237 line
238 }
239
240 fn slice_lines(&self, rows_range: Range<usize>) -> RopeSlice<'_> {
241 let start = self.line_start_offset(rows_range.start);
242 let end = self.line_end_offset(rows_range.end.saturating_sub(1));
243 self.slice(start..end)
244 }
245
246 fn iter_lines(&self) -> RopeLines<'_> {
247 RopeLines::new(&self)
248 }
249
250 fn line_len(&self, row: usize) -> usize {
251 self.slice_line(row).len()
252 }
253
254 fn line_start_offset(&self, row: usize) -> usize {
255 self.point_to_offset(Point::new(row, 0))
256 }
257
258 fn offset_to_point(&self, offset: usize) -> Point {
259 let offset = self.clip_offset(offset, Bias::Left);
260 let row = self.byte_to_line_idx(offset, LineType::LF);
261 let line_start = self.line_to_byte_idx(row, LineType::LF);
262 let column = offset.saturating_sub(line_start);
263 Point::new(row, column)
264 }
265
266 fn point_to_offset(&self, point: Point) -> usize {
267 if point.row >= self.lines_len() {
268 return self.len();
269 }
270
271 let line_start = self.line_to_byte_idx(point.row, LineType::LF);
272 line_start + point.column
273 }
274
275 fn position_to_offset(&self, pos: &Position) -> usize {
276 let line = self.slice_line(pos.line as usize);
277 self.line_start_offset(pos.line as usize)
278 + line
279 .chars()
280 .take(pos.character as usize)
281 .map(|c| c.len_utf8())
282 .sum::<usize>()
283 }
284
285 fn offset_to_position(&self, offset: usize) -> Position {
286 let point = self.offset_to_point(offset);
287 let line = self.slice_line(point.row);
288 let offset = line.utf16_to_byte_idx(line.byte_to_utf16_idx(point.column));
289 let character = line.slice(..offset).chars().count();
290 Position::new(point.row as u32, character as u32)
291 }
292
293 fn line_end_offset(&self, row: usize) -> usize {
294 if row > self.lines_len() {
295 return self.len();
296 }
297
298 self.line_start_offset(row) + self.line_len(row)
299 }
300
301 fn lines_len(&self) -> usize {
302 self.len_lines(LineType::LF)
303 }
304
305 fn char_at(&self, offset: usize) -> Option<char> {
306 if offset > self.len() {
307 return None;
308 }
309
310 self.get_char(offset).ok()
311 }
312
313 fn word_range(&self, offset: usize) -> Option<Range<usize>> {
314 if offset >= self.len() {
315 return None;
316 }
317
318 let mut left = String::new();
319 let offset = self.clip_offset(offset, Bias::Left);
320 for c in self.chars_at(offset).reversed() {
321 if c.is_alphanumeric() || c == '_' {
322 left.insert(0, c);
323 } else {
324 break;
325 }
326 }
327 let start = offset.saturating_sub(left.len());
328
329 let right = self
330 .chars_at(offset)
331 .take_while(|c| c.is_alphanumeric() || *c == '_')
332 .collect::<String>();
333
334 let end = offset + right.len();
335
336 if start == end {
337 None
338 } else {
339 Some(start..end)
340 }
341 }
342
343 fn word_at(&self, offset: usize) -> String {
344 if let Some(range) = self.word_range(offset) {
345 self.slice(range).to_string()
346 } else {
347 String::new()
348 }
349 }
350
351 #[inline]
352 fn offset_utf16_to_offset(&self, offset_utf16: usize) -> usize {
353 if offset_utf16 > self.len_utf16() {
354 return self.len();
355 }
356
357 self.utf16_to_byte_idx(offset_utf16)
358 }
359
360 #[inline]
361 fn offset_to_offset_utf16(&self, offset: usize) -> usize {
362 if offset > self.len() {
363 return self.len_utf16();
364 }
365
366 self.byte_to_utf16_idx(offset)
367 }
368
369 fn replace(&mut self, range: Range<usize>, new_text: &str) {
370 let range =
371 self.clip_offset(range.start, Bias::Left)..self.clip_offset(range.end, Bias::Right);
372 self.remove(range.clone());
373 self.insert(range.start, new_text);
374 }
375
376 fn clip_offset(&self, offset: usize, bias: Bias) -> usize {
377 if offset > self.len() {
378 return self.len();
379 }
380
381 if self.is_char_boundary(offset) {
382 return offset;
383 }
384
385 if bias == Bias::Left {
386 self.floor_char_boundary(offset)
387 } else {
388 self.ceil_char_boundary(offset)
389 }
390 }
391}
392
393#[cfg(test)]
394mod tests {
395 use ropey::Rope;
396 use sum_tree::Bias;
397 use tree_sitter::Point;
398
399 use crate::input::{Position, RopeExt};
400
401 #[test]
402 fn test_slice_line() {
403 let rope = Rope::from("Hello\nWorld\r\nThis is a test 中文\nRope");
404 assert_eq!(rope.slice_line(0).to_string(), "Hello");
405 assert_eq!(rope.slice_line(1).to_string(), "World\r");
406 assert_eq!(rope.slice_line(2).to_string(), "This is a test 中文");
407 assert_eq!(rope.slice_line(3).to_string(), "Rope");
408
409 assert_eq!(rope.slice_line(6).to_string(), "");
411
412 let rope = Rope::from("Hello\r");
414 assert_eq!(rope.slice_line(0).to_string(), "Hello\r");
415 assert_eq!(rope.slice_line(1).to_string(), "");
416 }
417
418 #[test]
419 fn test_lines_len() {
420 let rope = Rope::from("Hello\nWorld\r\nThis is a test 中文\nRope");
421 assert_eq!(rope.lines_len(), 4);
422 let rope = Rope::from("");
423 assert_eq!(rope.lines_len(), 1);
424 let rope = Rope::from("Single line");
425 assert_eq!(rope.lines_len(), 1);
426
427 let rope = Rope::from("Hello\r");
429 assert_eq!(rope.lines_len(), 1);
430 }
431
432 #[test]
433 fn test_lines() {
434 let rope = Rope::from("Hello\nWorld\r\nThis is a test 中文\nRope\r");
435 let lines: Vec<_> = rope.iter_lines().map(|r| r.to_string()).collect();
436 assert_eq!(
437 lines,
438 vec!["Hello", "World\r", "This is a test 中文", "Rope\r"]
439 );
440 }
441
442 #[test]
443 fn test_eq() {
444 let rope = Rope::from("Hello\nWorld\r\nThis is a test 中文\nRope");
445 assert!(rope.eq(&Rope::from("Hello\nWorld\r\nThis is a test 中文\nRope")));
446 assert!(!rope.eq(&Rope::from("Hello\nWorld")));
447
448 let rope1 = rope.clone();
449 assert!(rope.eq(&rope1));
450 }
451
452 #[test]
453 fn test_iter_lines() {
454 let rope = Rope::from("Hello\nWorld\r\nThis is a test 中文\nRope");
455 let lines: Vec<_> = rope
456 .iter_lines()
457 .skip(1)
458 .take(2)
459 .map(|r| r.to_string())
460 .collect();
461 assert_eq!(lines, vec!["World\r", "This is a test 中文"]);
462 }
463
464 #[test]
465 fn test_line_start_end_offset() {
466 let rope = Rope::from("Hello\nWorld\r\nThis is a test 中文\nRope");
467 assert_eq!(rope.line_start_offset(0), 0);
468 assert_eq!(rope.line_end_offset(0), 5);
469
470 assert_eq!(rope.line_start_offset(1), 6);
471 assert_eq!(rope.line_end_offset(1), 12);
472
473 assert_eq!(rope.line_start_offset(2), 13);
474 assert_eq!(rope.line_end_offset(2), 34);
475
476 assert_eq!(rope.line_start_offset(3), 35);
477 assert_eq!(rope.line_end_offset(3), 39);
478
479 assert_eq!(rope.line_start_offset(4), 39);
480 assert_eq!(rope.line_end_offset(4), 39);
481 }
482
483 #[test]
484 fn test_line_column() {
485 let rope = Rope::from("a 中文🎉 test\nRope");
486 assert_eq!(rope.position_to_offset(&Position::new(0, 3)), "a 中".len());
487 assert_eq!(
488 rope.position_to_offset(&Position::new(0, 5)),
489 "a 中文🎉".len()
490 );
491 assert_eq!(
492 rope.position_to_offset(&Position::new(1, 1)),
493 "a 中文🎉 test\nR".len()
494 );
495
496 assert_eq!(
497 rope.offset_to_position("a 中文🎉 test\nR".len()),
498 Position::new(1, 1)
499 );
500 assert_eq!(
501 rope.offset_to_position("a 中文🎉".len()),
502 Position::new(0, 5)
503 );
504 }
505
506 #[test]
507 fn test_offset_to_point() {
508 let rope = Rope::from("a 中文🎉 test\nRope");
509 assert_eq!(rope.offset_to_point(0), Point::new(0, 0));
510 assert_eq!(rope.offset_to_point(1), Point::new(0, 1));
511 assert_eq!(rope.offset_to_point("a 中".len()), Point::new(0, 5));
512 assert_eq!(rope.offset_to_point("a 中文🎉".len()), Point::new(0, 12));
513 assert_eq!(
514 rope.offset_to_point("a 中文🎉 test\nR".len()),
515 Point::new(1, 1)
516 );
517 }
518
519 #[test]
520 fn test_point_to_offset() {
521 let rope = Rope::from("a 中文🎉 test\nRope");
522 assert_eq!(rope.point_to_offset(Point::new(0, 0)), 0);
523 assert_eq!(rope.point_to_offset(Point::new(0, 1)), 1);
524 assert_eq!(rope.point_to_offset(Point::new(0, 5)), "a 中".len());
525 assert_eq!(rope.point_to_offset(Point::new(0, 12)), "a 中文🎉".len());
526 assert_eq!(
527 rope.point_to_offset(Point::new(1, 1)),
528 "a 中文🎉 test\nR".len()
529 );
530 }
531
532 #[test]
533 fn test_char_at() {
534 let rope = Rope::from("Hello\nWorld\r\nThis is a test 中文🎉\nRope");
535 assert_eq!(rope.char_at(0), Some('H'));
536 assert_eq!(rope.char_at(5), Some('\n'));
537 assert_eq!(rope.char_at(13), Some('T'));
538 assert_eq!(rope.char_at(28), Some('中'));
539 assert_eq!(rope.char_at(34), Some('🎉'));
540 assert_eq!(rope.char_at(38), Some('\n'));
541 assert_eq!(rope.char_at(50), None);
542 }
543
544 #[test]
545 fn test_word_at() {
546 let rope = Rope::from("Hello\nWorld\r\nThis is a test 中文 世界\nRope");
547 assert_eq!(rope.word_at(0), "Hello");
548 assert_eq!(rope.word_range(0), Some(0..5));
549 assert_eq!(rope.word_at(8), "World");
550 assert_eq!(rope.word_range(8), Some(6..11));
551 assert_eq!(rope.word_at(12), "");
552 assert_eq!(rope.word_range(12), None);
553 assert_eq!(rope.word_at(13), "This");
554 assert_eq!(rope.word_range(13), Some(13..17));
555 assert_eq!(rope.word_at(31), "中文");
556 assert_eq!(rope.word_range(31), Some(28..34));
557 assert_eq!(rope.word_at(38), "世界");
558 assert_eq!(rope.word_range(38), Some(35..41));
559 assert_eq!(rope.word_at(44), "Rope");
560 assert_eq!(rope.word_range(44), Some(42..46));
561 assert_eq!(rope.word_at(45), "Rope");
562 }
563
564 #[test]
565 fn test_offset_utf16_conversion() {
566 let rope = Rope::from("hello 中文🎉 test\nRope");
567 assert_eq!(rope.offset_to_offset_utf16("hello".len()), 5);
568 assert_eq!(rope.offset_to_offset_utf16("hello 中".len()), 7);
569 assert_eq!(rope.offset_to_offset_utf16("hello 中文".len()), 8);
570 assert_eq!(rope.offset_to_offset_utf16("hello 中文🎉".len()), 10);
571 assert_eq!(rope.offset_to_offset_utf16(100), 20);
572
573 assert_eq!(rope.offset_utf16_to_offset(5), "hello".len());
574 assert_eq!(rope.offset_utf16_to_offset(7), "hello 中".len());
575 assert_eq!(rope.offset_utf16_to_offset(8), "hello 中文".len());
576 assert_eq!(rope.offset_utf16_to_offset(10), "hello 中文🎉".len());
577 assert_eq!(rope.offset_utf16_to_offset(100), rope.len());
578 }
579
580 #[test]
581 fn test_replace() {
582 let mut rope = Rope::from("Hello\nWorld\r\nThis is a test 中文\nRope");
583 rope.replace(6..11, "Universe");
584 assert_eq!(
585 rope.to_string(),
586 "Hello\nUniverse\r\nThis is a test 中文\nRope"
587 );
588
589 rope.replace(0..5, "Hi");
590 assert_eq!(
591 rope.to_string(),
592 "Hi\nUniverse\r\nThis is a test 中文\nRope"
593 );
594
595 rope.replace(rope.len() - 4..rope.len(), "String");
596 assert_eq!(
597 rope.to_string(),
598 "Hi\nUniverse\r\nThis is a test 中文\nString"
599 );
600
601 let mut rope = Rope::from("中文");
603 rope.replace(0..1, "New");
604 assert_eq!(rope.to_string(), "New文");
605 let mut rope = Rope::from("中文");
606 rope.replace(0..2, "New");
607 assert_eq!(rope.to_string(), "New文");
608 let mut rope = Rope::from("中文");
609 rope.replace(0..3, "New");
610 assert_eq!(rope.to_string(), "New文");
611 let mut rope = Rope::from("中文");
612 rope.replace(1..4, "New");
613 assert_eq!(rope.to_string(), "New");
614 }
615
616 #[test]
617 fn test_clip_offset() {
618 let rope = Rope::from("Hello 中文🎉 test\nRope");
619 assert_eq!(rope.clip_offset(5, Bias::Left), 5);
621 assert_eq!(rope.clip_offset(7, Bias::Left), 6);
622 assert_eq!(rope.clip_offset(7, Bias::Right), 9);
623 assert_eq!(rope.clip_offset(9, Bias::Left), 9);
624
625 assert_eq!(rope.clip_offset(13, Bias::Left), 12);
627 assert_eq!(rope.clip_offset(13, Bias::Right), 16);
628 assert_eq!(rope.clip_offset(16, Bias::Left), 16);
629
630 assert_eq!(rope.clip_offset(5, Bias::Left), 5);
632 assert_eq!(rope.clip_offset(5, Bias::Right), 5);
633
634 assert_eq!(rope.clip_offset(26, Bias::Left), 26);
636 assert_eq!(rope.clip_offset(100, Bias::Left), 26);
637 }
638}