1use std::{borrow::Cow, ops::Range};
2
3use lapce_xi_rope::{interval::IntervalBounds, rope::ChunkIter, Cursor, Rope};
4
5use crate::{mode::Mode, paragraph::ParagraphCursor, word::WordCursor};
6
7pub trait RopeText {
8 fn text(&self) -> &Rope;
9
10 fn len(&self) -> usize {
11 self.text().len()
12 }
13
14 fn is_empty(&self) -> bool {
15 self.len() == 0
16 }
17
18 fn last_line(&self) -> usize {
20 self.line_of_offset(self.len())
21 }
22
23 fn offset_of_line(&self, line: usize) -> usize {
26 let last_line = self.last_line();
27 let line = line.min(last_line + 1);
28 self.text().offset_of_line(line)
29 }
30
31 fn offset_line_end(&self, offset: usize, caret: bool) -> usize {
32 let line = self.line_of_offset(offset);
33 self.line_end_offset(line, caret)
34 }
35
36 fn line_of_offset(&self, offset: usize) -> usize {
37 let offset = offset.min(self.len());
38 let offset = self
39 .text()
40 .at_or_prev_codepoint_boundary(offset)
41 .unwrap_or(offset);
42
43 self.text().line_of_offset(offset)
44 }
45
46 fn offset_to_line_col(&self, offset: usize) -> (usize, usize) {
47 let offset = offset.min(self.len());
48 let line = self.line_of_offset(offset);
49 let line_start = self.offset_of_line(line);
50 if offset == line_start {
51 return (line, 0);
52 }
53
54 let col = offset - line_start;
55 (line, col)
56 }
57
58 fn offset_of_line_col(&self, line: usize, col: usize) -> usize {
82 let mut pos = 0;
83 let mut offset = self.offset_of_line(line);
84 let text = self.slice_to_cow(offset..self.offset_of_line(line + 1));
85 let mut iter = text.chars().peekable();
86 while let Some(c) = iter.next() {
87 if c == '\n' || (c == '\r' && iter.peek() == Some(&'\n')) {
89 return offset;
90 }
91
92 let char_len = c.len_utf8();
93 if pos + char_len > col {
94 return offset;
95 }
96 pos += char_len;
97 offset += char_len;
98 }
99 offset
100 }
101
102 fn line_end_col(&self, line: usize, caret: bool) -> usize {
103 let line_start = self.offset_of_line(line);
104 let offset = self.line_end_offset(line, caret);
105 offset - line_start
106 }
107
108 fn line_end_offset(&self, line: usize, caret: bool) -> usize {
124 let mut offset = self.offset_of_line(line + 1);
125 let mut line_content: &str = &self.line_content(line);
126 if line_content.ends_with("\r\n") {
127 offset -= 2;
128 line_content = &line_content[..line_content.len() - 2];
129 } else if line_content.ends_with('\n') {
130 offset -= 1;
131 line_content = &line_content[..line_content.len() - 1];
132 }
133 if !caret && !line_content.is_empty() {
134 offset = self.prev_grapheme_offset(offset, 1, 0);
135 }
136 offset
137 }
138
139 fn line_content(&self, line: usize) -> Cow<'_, str> {
143 self.text()
144 .slice_to_cow(self.offset_of_line(line)..self.offset_of_line(line + 1))
145 }
146
147 fn prev_grapheme_offset(&self, offset: usize, count: usize, limit: usize) -> usize {
149 let offset = offset.min(self.len());
150 let mut cursor = Cursor::new(self.text(), offset);
151 let mut new_offset = offset;
152 for _i in 0..count {
153 if let Some(prev_offset) = cursor.prev_grapheme() {
154 if prev_offset < limit {
155 return new_offset;
156 }
157 new_offset = prev_offset;
158 cursor.set(prev_offset);
159 } else {
160 return new_offset;
161 }
162 }
163 new_offset
164 }
165
166 fn next_grapheme_offset(&self, offset: usize, count: usize, limit: usize) -> usize {
167 let offset = if offset > self.len() {
168 self.len()
169 } else {
170 offset
171 };
172 let mut cursor = Cursor::new(self.text(), offset);
173 let mut new_offset = offset;
174 for _i in 0..count {
175 if let Some(next_offset) = cursor.next_grapheme() {
176 if next_offset > limit {
177 return new_offset;
178 }
179 new_offset = next_offset;
180 cursor.set(next_offset);
181 } else {
182 return new_offset;
183 }
184 }
185 new_offset
186 }
187
188 fn prev_code_boundary(&self, offset: usize) -> usize {
189 WordCursor::new(self.text(), offset).prev_code_boundary()
190 }
191
192 fn next_code_boundary(&self, offset: usize) -> usize {
193 WordCursor::new(self.text(), offset).next_code_boundary()
194 }
195
196 fn select_word(&self, offset: usize) -> (usize, usize) {
198 WordCursor::new(self.text(), offset).select_word()
199 }
200
201 fn first_non_blank_character_on_line(&self, line: usize) -> usize {
205 let last_line = self.last_line();
206 let line = if line > last_line + 1 {
207 last_line
208 } else {
209 line
210 };
211 let line_start_offset = self.text().offset_of_line(line);
212 WordCursor::new(self.text(), line_start_offset).next_non_blank_char()
213 }
214
215 fn indent_on_line(&self, line: usize) -> String {
216 let line_start_offset = self.text().offset_of_line(line);
217 let word_boundary = WordCursor::new(self.text(), line_start_offset).next_non_blank_char();
218 let indent = self.text().slice_to_cow(line_start_offset..word_boundary);
219 indent.to_string()
220 }
221
222 fn slice_to_cow(&self, range: Range<usize>) -> Cow<'_, str> {
227 self.text()
228 .slice_to_cow(range.start.min(self.len())..range.end.min(self.len()))
229 }
230
231 #[allow(clippy::type_complexity)]
234 fn char_indices_iter<'a, T: IntervalBounds>(
236 &'a self,
237 range: T,
238 ) -> CharIndicesJoin<
239 std::str::CharIndices<'a>,
240 std::iter::Map<ChunkIter<'a>, fn(&str) -> std::str::CharIndices<'_>>,
241 > {
242 let iter: ChunkIter<'a> = self.text().iter_chunks(range);
243 let iter: std::iter::Map<ChunkIter<'a>, fn(&str) -> std::str::CharIndices<'_>> =
244 iter.map(str::char_indices);
245 CharIndicesJoin::new(iter)
246 }
247
248 fn num_lines(&self) -> usize {
250 self.last_line() + 1
251 }
252
253 fn line_len(&self, line: usize) -> usize {
255 self.offset_of_line(line + 1) - self.offset_of_line(line)
256 }
257
258 fn is_line_whitespace(&self, line: usize) -> bool {
260 let line_start_offset = self.text().offset_of_line(line);
261 let mut word_cursor = WordCursor::new(self.text(), line_start_offset);
262
263 word_cursor.next_non_blank_char();
264 let c = word_cursor.inner.next_codepoint();
265
266 match c {
267 None | Some('\n') => true,
268 Some('\r') => {
269 let c = word_cursor.inner.next_codepoint();
270 c.is_some_and(|c| c == '\n')
271 }
272 _ => false,
273 }
274 }
275
276 fn move_left(&self, offset: usize, mode: Mode, count: usize) -> usize {
277 let min_offset = if mode == Mode::Insert {
278 0
279 } else {
280 let line = self.line_of_offset(offset);
281 self.offset_of_line(line)
282 };
283
284 self.prev_grapheme_offset(offset, count, min_offset)
285 }
286
287 fn move_right(&self, offset: usize, mode: Mode, count: usize) -> usize {
288 let max_offset = if mode == Mode::Insert {
289 self.len()
290 } else {
291 self.offset_line_end(offset, mode != Mode::Normal)
292 };
293
294 self.next_grapheme_offset(offset, count, max_offset)
295 }
296
297 fn find_nth_paragraph<F>(&self, offset: usize, mut count: usize, mut find_next: F) -> usize
298 where
299 F: FnMut(&mut ParagraphCursor) -> Option<usize>,
300 {
301 let mut cursor = ParagraphCursor::new(self.text(), offset);
302 let mut new_offset = offset;
303 while count != 0 {
304 if let Some(offset) = find_next(&mut cursor) {
306 new_offset = offset;
307 } else {
308 break;
309 }
310 count -= 1;
311 }
312 new_offset
313 }
314
315 fn move_n_paragraphs_forward(&self, offset: usize, count: usize) -> usize {
316 self.find_nth_paragraph(offset, count, |cursor| cursor.next_boundary())
317 }
318
319 fn move_n_paragraphs_backward(&self, offset: usize, count: usize) -> usize {
320 self.find_nth_paragraph(offset, count, |cursor| cursor.prev_boundary())
321 }
322
323 fn find_nth_word<F>(&self, offset: usize, mut count: usize, mut find_next: F) -> usize
331 where
332 F: FnMut(&mut WordCursor) -> Option<usize>,
333 {
334 let mut cursor = WordCursor::new(self.text(), offset);
335 let mut new_offset = offset;
336 while count != 0 {
337 if let Some(offset) = find_next(&mut cursor) {
339 new_offset = offset;
340 } else {
341 break;
342 }
343 count -= 1;
344 }
345 new_offset
346 }
347
348 fn move_n_words_forward(&self, offset: usize, count: usize) -> usize {
349 self.find_nth_word(offset, count, |cursor| cursor.next_boundary())
350 }
351
352 fn move_n_wordends_forward(&self, offset: usize, count: usize, inserting: bool) -> usize {
353 let mut new_offset = self.find_nth_word(offset, count, |cursor| cursor.end_boundary());
354 if !inserting && new_offset != self.len() {
355 new_offset = self.prev_grapheme_offset(new_offset, 1, 0);
356 }
357 new_offset
358 }
359
360 fn move_n_words_backward(&self, offset: usize, count: usize, mode: Mode) -> usize {
361 self.find_nth_word(offset, count, |cursor| cursor.prev_boundary(mode))
362 }
363
364 fn move_word_backward_deletion(&self, offset: usize) -> usize {
365 self.find_nth_word(offset, 1, |cursor| cursor.prev_deletion_boundary())
366 }
367}
368
369#[derive(Clone)]
370pub struct RopeTextVal {
371 pub text: Rope,
372}
373impl RopeTextVal {
374 pub fn new(text: Rope) -> Self {
375 Self { text }
376 }
377}
378impl RopeText for RopeTextVal {
379 fn text(&self) -> &Rope {
380 &self.text
381 }
382}
383impl From<Rope> for RopeTextVal {
384 fn from(text: Rope) -> Self {
385 Self::new(text)
386 }
387}
388#[derive(Copy, Clone)]
389pub struct RopeTextRef<'a> {
390 pub text: &'a Rope,
391}
392impl<'a> RopeTextRef<'a> {
393 pub fn new(text: &'a Rope) -> Self {
394 Self { text }
395 }
396}
397impl RopeText for RopeTextRef<'_> {
398 fn text(&self) -> &Rope {
399 self.text
400 }
401}
402impl<'a> From<&'a Rope> for RopeTextRef<'a> {
403 fn from(text: &'a Rope) -> Self {
404 Self::new(text)
405 }
406}
407
408impl RopeText for Rope {
409 fn text(&self) -> &Rope {
410 self
411 }
412}
413
414#[derive(Clone)]
418pub struct CharIndicesJoin<I: Iterator<Item = (usize, char)>, O: Iterator<Item = I>> {
419 main_iter: O,
421 current_indices: Option<I>,
423 current_base: usize,
425 latest_base: usize,
427}
428
429impl<I: Iterator<Item = (usize, char)>, O: Iterator<Item = I>> CharIndicesJoin<I, O> {
430 pub fn new(main_iter: O) -> CharIndicesJoin<I, O> {
431 CharIndicesJoin {
432 main_iter,
433 current_indices: None,
434 current_base: 0,
435 latest_base: 0,
436 }
437 }
438}
439
440impl<I: Iterator<Item = (usize, char)>, O: Iterator<Item = I>> Iterator for CharIndicesJoin<I, O> {
441 type Item = (usize, char);
442
443 fn next(&mut self) -> Option<Self::Item> {
444 if let Some(current) = &mut self.current_indices {
445 if let Some((next_offset, next_ch)) = current.next() {
446 let next_offset = self.current_base + next_offset;
449 self.latest_base = next_offset + next_ch.len_utf8();
452 return Some((next_offset, next_ch));
453 }
454 }
455
456 if let Some(next_current) = self.main_iter.next() {
458 self.current_indices = Some(next_current);
460 self.current_base = self.latest_base;
463
464 self.next()
469 } else {
470 None
472 }
473 }
474}
475
476#[cfg(test)]
477mod tests {
478 use lapce_xi_rope::Rope;
479
480 use super::RopeText;
481 use crate::buffer::rope_text::RopeTextVal;
482
483 #[test]
484 fn test_line_content() {
485 let text = Rope::from("");
486 let text = RopeTextVal::new(text);
487
488 assert_eq!(text.line_content(0), "");
489 assert_eq!(text.line_content(1), "");
490 assert_eq!(text.line_content(2), "");
491
492 let text = Rope::from("abc\ndef\nghi");
493 let text = RopeTextVal::new(text);
494
495 assert_eq!(text.line_content(0), "abc\n");
496 assert_eq!(text.line_content(1), "def\n");
497 assert_eq!(text.line_content(2), "ghi");
498 assert_eq!(text.line_content(3), "");
499 assert_eq!(text.line_content(4), "");
500 assert_eq!(text.line_content(5), "");
501
502 let text = Rope::from("abc\r\ndef\r\nghi");
503 let text = RopeTextVal::new(text);
504
505 assert_eq!(text.line_content(0), "abc\r\n");
506 assert_eq!(text.line_content(1), "def\r\n");
507 assert_eq!(text.line_content(2), "ghi");
508 assert_eq!(text.line_content(3), "");
509 assert_eq!(text.line_content(4), "");
510 assert_eq!(text.line_content(5), "");
511 }
512
513 #[test]
514 fn test_offset_of_line() {
515 let text = Rope::from("");
516 let text = RopeTextVal::new(text);
517
518 assert_eq!(text.offset_of_line(0), 0);
519 assert_eq!(text.offset_of_line(1), 0);
520 assert_eq!(text.offset_of_line(2), 0);
521
522 let text = Rope::from("abc\ndef\nghi");
523 let text = RopeTextVal::new(text);
524
525 assert_eq!(text.offset_of_line(0), 0);
526 assert_eq!(text.offset_of_line(1), 4);
527 assert_eq!(text.offset_of_line(2), 8);
528 assert_eq!(text.offset_of_line(3), text.len()); assert_eq!(text.offset_of_line(4), text.len());
530 assert_eq!(text.offset_of_line(5), text.len());
531
532 let text = Rope::from("abc\r\ndef\r\nghi");
533 let text = RopeTextVal::new(text);
534
535 assert_eq!(text.offset_of_line(0), 0);
536 assert_eq!(text.offset_of_line(1), 5);
537 assert_eq!(text.offset_of_line(2), 10);
538 assert_eq!(text.offset_of_line(3), text.len()); assert_eq!(text.offset_of_line(4), text.len());
540 assert_eq!(text.offset_of_line(5), text.len());
541 }
542
543 #[test]
544 fn test_offset_of_line_col() {
545 let text = Rope::from("abc\ndef\nghi");
546 let text = RopeTextVal::new(text);
547
548 assert_eq!(text.offset_of_line_col(0, 0), 0);
549 assert_eq!(text.offset_of_line_col(0, 1), 1);
550 assert_eq!(text.offset_of_line_col(0, 2), 2);
551 assert_eq!(text.offset_of_line_col(0, 3), 3);
552 assert_eq!(text.offset_of_line_col(0, 4), 3);
553 assert_eq!(text.offset_of_line_col(1, 0), 4);
554
555 let text = Rope::from("abc\r\ndef\r\nghi");
556 let text = RopeTextVal::new(text);
557
558 assert_eq!(text.offset_of_line_col(0, 0), 0);
559 assert_eq!(text.offset_of_line_col(0, 1), 1);
560 assert_eq!(text.offset_of_line_col(0, 2), 2);
561 assert_eq!(text.offset_of_line_col(0, 3), 3);
562 assert_eq!(text.offset_of_line_col(0, 4), 3);
563 assert_eq!(text.offset_of_line_col(1, 0), 5);
564 }
565
566 #[test]
567 fn test_line_end_offset() {
568 let text = Rope::from("");
569 let text = RopeTextVal::new(text);
570
571 assert_eq!(text.line_end_offset(0, false), 0);
572 assert_eq!(text.line_end_offset(0, true), 0);
573 assert_eq!(text.line_end_offset(1, false), 0);
574 assert_eq!(text.line_end_offset(1, true), 0);
575 assert_eq!(text.line_end_offset(2, false), 0);
576 assert_eq!(text.line_end_offset(2, true), 0);
577
578 let text = Rope::from("abc\ndef\nghi");
579 let text = RopeTextVal::new(text);
580
581 assert_eq!(text.line_end_offset(0, false), 2);
582 assert_eq!(text.line_end_offset(0, true), 3);
583 assert_eq!(text.line_end_offset(1, false), 6);
584 assert_eq!(text.line_end_offset(1, true), 7);
585 assert_eq!(text.line_end_offset(2, false), 10);
586 assert_eq!(text.line_end_offset(2, true), text.len());
587 assert_eq!(text.line_end_offset(3, false), text.len());
588 assert_eq!(text.line_end_offset(3, true), text.len());
589 assert_eq!(text.line_end_offset(4, false), text.len());
590 assert_eq!(text.line_end_offset(4, true), text.len());
591 }
592
593 #[test]
594 fn test_prev_grapheme_offset() {
595 let text = Rope::from("");
596 let text = RopeTextVal::new(text);
597
598 assert_eq!(text.prev_grapheme_offset(0, 0, 0), 0);
599 assert_eq!(text.prev_grapheme_offset(0, 1, 0), 0);
600 assert_eq!(text.prev_grapheme_offset(0, 1, 1), 0);
601
602 let text = Rope::from("abc def ghi");
603 let text = RopeTextVal::new(text);
604
605 assert_eq!(text.prev_grapheme_offset(0, 0, 0), 0);
606 assert_eq!(text.prev_grapheme_offset(0, 1, 0), 0);
607 assert_eq!(text.prev_grapheme_offset(0, 1, 1), 0);
608 assert_eq!(text.prev_grapheme_offset(2, 1, 0), 1);
609 assert_eq!(text.prev_grapheme_offset(2, 1, 1), 1);
610 }
611
612 #[test]
613 fn test_first_non_blank_character_on_line() {
614 let text = Rope::from("");
615 let text = RopeTextVal::new(text);
616
617 assert_eq!(text.first_non_blank_character_on_line(0), 0);
618 assert_eq!(text.first_non_blank_character_on_line(1), 0);
619 assert_eq!(text.first_non_blank_character_on_line(2), 0);
620
621 let text = Rope::from("abc\ndef\nghi");
622 let text = RopeTextVal::new(text);
623
624 assert_eq!(text.first_non_blank_character_on_line(0), 0);
625 assert_eq!(text.first_non_blank_character_on_line(1), 4);
626 assert_eq!(text.first_non_blank_character_on_line(2), 8);
627 assert_eq!(text.first_non_blank_character_on_line(3), 11);
628 assert_eq!(text.first_non_blank_character_on_line(4), 8);
629 assert_eq!(text.first_non_blank_character_on_line(5), 8);
630
631 let text = Rope::from("abc\r\ndef\r\nghi");
632 let text = RopeTextVal::new(text);
633
634 assert_eq!(text.first_non_blank_character_on_line(0), 0);
635 assert_eq!(text.first_non_blank_character_on_line(1), 5);
636 assert_eq!(text.first_non_blank_character_on_line(2), 10);
637 assert_eq!(text.first_non_blank_character_on_line(3), 13);
638 assert_eq!(text.first_non_blank_character_on_line(4), 10);
639 assert_eq!(text.first_non_blank_character_on_line(5), 10);
640 }
641
642 #[test]
643 fn test_is_line_whitespace() {
644 let text = Rope::from("");
645 let text = RopeTextVal::new(text);
646
647 assert!(text.is_line_whitespace(0));
648
649 let text = Rope::from("\n \t\r\t \t \n");
650 let text = RopeTextVal::new(text);
651
652 assert!(text.is_line_whitespace(0));
653 assert!(!text.is_line_whitespace(1));
654 assert!(text.is_line_whitespace(2));
655
656 let text = Rope::from("qwerty\n\tf\t\r\n00");
657 let text = RopeTextVal::new(text);
658
659 assert!(!text.is_line_whitespace(0));
660 assert!(!text.is_line_whitespace(1));
661 assert!(!text.is_line_whitespace(2));
662
663 let text = Rope::from(" \r#\n\t \r\n)\t\t\t\t\t\t\t\t");
664 let text = RopeTextVal::new(text);
665
666 assert!(!text.is_line_whitespace(0));
667 assert!(text.is_line_whitespace(1));
668 assert!(!text.is_line_whitespace(2));
669
670 let text = Rope::from(" \r\n \r");
671 let text = RopeTextVal::new(text);
672
673 assert!(text.is_line_whitespace(0));
674 assert!(!text.is_line_whitespace(1));
675 }
676}