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