1use crate::grapheme::{RopeGraphemes, StrGraphemes};
2use crate::text_store::{Cursor, TextStore};
3use crate::{TextError, TextPosition, TextRange, upos_type};
4use ropey::{Rope, RopeSlice};
5use std::borrow::Cow;
6use std::cell::Cell;
7use std::cmp::min;
8use std::ops::Range;
9use unicode_segmentation::UnicodeSegmentation;
10
11#[derive(Debug, Clone, Default)]
13pub struct TextRope {
14 text: Rope,
15 min_changed: Cell<Option<usize>>,
17 buf: String,
19}
20
21impl TextRope {
22 pub fn new() -> Self {
24 Self::default()
25 }
26
27 pub fn new_text(t: &str) -> Self {
29 Self {
30 text: Rope::from_str(t),
31 min_changed: Default::default(),
32 buf: Default::default(),
33 }
34 }
35
36 pub fn new_rope(r: Rope) -> Self {
38 Self {
39 text: r,
40 min_changed: Default::default(),
41 buf: Default::default(),
42 }
43 }
44
45 pub fn rope(&self) -> &Rope {
47 &self.text
48 }
49
50 #[inline]
52 #[deprecated]
53 pub fn rope_slice(&self, range: TextRange) -> Result<RopeSlice<'_>, TextError> {
54 let s = self.byte_range(range)?;
55 Ok(self.text.get_byte_slice(s).expect("valid_range"))
56 }
57}
58
59impl TextRope {
60 fn invalidate(&self, byte_pos: usize) {
61 self.min_changed.update(|v| match v {
62 None => Some(byte_pos),
63 Some(w) => Some(min(byte_pos, w)),
64 });
65 }
66
67 #[inline]
68 #[allow(clippy::match_like_matches_macro)]
69 fn has_final_newline(&self) -> bool {
70 let len = self.text.len_bytes();
71 if len > 3 {
72 match (
73 self.text.get_byte(len - 3).expect("valid_pos"),
74 self.text.get_byte(len - 2).expect("valid_pos"),
75 self.text.get_byte(len - 1).expect("valid_pos"),
76 ) {
77 (_, _, b'\n')
78 | (_, _, b'\r')
79 | (_, _, 0x0c)
80 | (_, _, 0x0b)
81 | (_, _, 0x85)
82 | (0xE2, 0x80, 0xA8)
83 | (0xE2, 0x80, 0xA9) => true,
84 _ => false,
85 }
86 } else if len > 0 {
87 match self.text.get_byte(len - 1).expect("valid_pos") {
88 b'\n' | b'\r' | 0x0c | 0x0b | 0x85 => true,
89 _ => false,
90 }
91 } else {
92 false
93 }
94 }
95
96 fn normalize_row(&self, row: upos_type) -> Result<upos_type, TextError> {
97 let text_len = self.len_lines() as upos_type;
98 let rope_len = self.text.len_lines() as upos_type;
99
100 if row <= rope_len {
101 Ok(row)
102 } else if row <= text_len {
103 Ok(row - 1)
104 } else {
105 Err(TextError::LineIndexOutOfBounds(row, text_len))
106 }
107 }
108
109 fn normalize(&self, pos: TextPosition) -> Result<(TextPosition, usize), TextError> {
110 let len = self.len_lines();
111 if pos.y > len {
112 Err(TextError::LineIndexOutOfBounds(pos.y, len))
113 } else if pos.x > 0 && pos.y == len {
114 Err(TextError::ColumnIndexOutOfBounds(pos.x, 0))
115 } else if pos.x > 0 && pos.y == len - 1 && !self.has_final_newline() {
116 Err(TextError::ColumnIndexOutOfBounds(pos.x, 0))
117 } else if pos.x == 0 && pos.y == len {
118 let pos_byte = self.byte_range_at(pos)?;
119 Ok((
120 self.byte_to_pos(pos_byte.start).expect("valid-byte"),
121 pos_byte.start,
122 ))
123 } else if pos.x == 0 && pos.y == len - 1 && !self.has_final_newline() {
124 let pos_byte = self.byte_range_at(pos)?;
125 Ok((
126 self.byte_to_pos(pos_byte.start).expect("valid-byte"),
127 pos_byte.start,
128 ))
129 } else {
130 let pos_byte = self.byte_range_at(pos)?;
131 Ok((pos, pos_byte.start))
132 }
133 }
134}
135
136impl TextStore for TextRope {
137 type GraphemeIter<'a> = RopeGraphemes<'a>;
138
139 fn is_multi_line(&self) -> bool {
144 true
145 }
146
147 fn cache_validity(&self) -> Option<usize> {
152 self.min_changed.take()
153 }
154
155 fn string(&self) -> String {
157 self.text.to_string()
158 }
159
160 fn set_string(&mut self, t: &str) {
162 self.invalidate(0);
163 self.text = Rope::from_str(t);
164 }
165
166 fn byte_range_at(&self, pos: TextPosition) -> Result<Range<usize>, TextError> {
171 let it_line = self.line_graphemes(pos.y)?;
172
173 let mut col = 0;
174 let mut byte_end = it_line.text_offset();
175 for grapheme in it_line {
176 if col == pos.x {
177 return Ok(grapheme.text_bytes());
178 }
179 col += 1;
180 byte_end = grapheme.text_bytes().end;
181 }
182 if col == pos.x {
184 Ok(byte_end..byte_end)
185 } else {
186 Err(TextError::ColumnIndexOutOfBounds(pos.x, col))
187 }
188 }
189
190 fn byte_range(&self, range: TextRange) -> Result<Range<usize>, TextError> {
194 if range.start.y == range.end.y {
195 let it_line = self.line_graphemes(range.start.y)?;
196
197 let mut range_start = None;
198 let mut range_end = None;
199 let mut col = 0;
200 let mut byte_end = it_line.text_offset();
201 for grapheme in it_line {
202 if col == range.start.x {
203 range_start = Some(grapheme.text_bytes().start);
204 }
205 if col == range.end.x {
206 range_end = Some(grapheme.text_bytes().end);
207 }
208 if range_start.is_some() && range_end.is_some() {
209 break;
210 }
211 col += 1;
212 byte_end = grapheme.text_bytes().end;
213 }
214 if col == range.start.x {
216 range_start = Some(byte_end);
217 }
218 if col == range.end.x {
219 range_end = Some(byte_end);
220 }
221
222 let Some(range_start) = range_start else {
223 return Err(TextError::ColumnIndexOutOfBounds(range.start.x, col));
224 };
225 let Some(range_end) = range_end else {
226 return Err(TextError::ColumnIndexOutOfBounds(range.end.x, col));
227 };
228
229 Ok(range_start..range_end)
230 } else {
231 let range_start = self.byte_range_at(range.start)?;
232 let range_end = self.byte_range_at(range.end)?;
233
234 Ok(range_start.start..range_end.start)
235 }
236 }
237
238 fn byte_to_pos(&self, byte_pos: usize) -> Result<TextPosition, TextError> {
243 let Ok(row) = self.text.try_byte_to_line(byte_pos) else {
244 return Err(TextError::ByteIndexOutOfBounds(
245 byte_pos,
246 self.text.len_bytes(),
247 ));
248 };
249 let row = row as upos_type;
250
251 let mut col = 0;
252 let it_line = self.line_graphemes(row)?;
253 for grapheme in it_line {
254 if byte_pos < grapheme.text_bytes().end {
255 break;
256 }
257 col += 1;
258 }
259
260 Ok(TextPosition::new(col, row))
261 }
262
263 fn bytes_to_range(&self, bytes: Range<usize>) -> Result<TextRange, TextError> {
267 let Ok(start_row) = self.text.try_byte_to_line(bytes.start) else {
268 return Err(TextError::ByteIndexOutOfBounds(
269 bytes.start,
270 self.text.len_bytes(),
271 ));
272 };
273 let start_row = start_row as upos_type;
274 let Ok(end_row) = self.text.try_byte_to_line(bytes.end) else {
275 return Err(TextError::ByteIndexOutOfBounds(
276 bytes.end,
277 self.text.len_bytes(),
278 ));
279 };
280 let end_row = end_row as upos_type;
281
282 if start_row == end_row {
283 let mut col = 0;
284 let mut start = None;
285 let mut end = None;
286 let it_line = self.line_graphemes(start_row)?;
287 for grapheme in it_line {
288 if bytes.start < grapheme.text_bytes().end {
289 if start.is_none() {
290 start = Some(col);
291 }
292 }
293 if bytes.end < grapheme.text_bytes().end {
294 if end.is_none() {
295 end = Some(col);
296 }
297 }
298 if start.is_some() && end.is_some() {
299 break;
300 }
301 col += 1;
302 }
303 if bytes.start == self.text.len_bytes() {
304 start = Some(col);
305 }
306 if bytes.end == self.text.len_bytes() {
307 end = Some(col);
308 }
309
310 let Some(start) = start else {
311 return Err(TextError::ByteIndexOutOfBounds(
312 bytes.start,
313 self.text.len_bytes(),
314 ));
315 };
316 let Some(end) = end else {
317 return Err(TextError::ByteIndexOutOfBounds(
318 bytes.end,
319 self.text.len_bytes(),
320 ));
321 };
322
323 Ok(TextRange::new((start, start_row), (end, end_row)))
324 } else {
325 let start = self.byte_to_pos(bytes.start)?;
326 let end = self.byte_to_pos(bytes.end)?;
327
328 Ok(TextRange::new(start, end))
329 }
330 }
331
332 fn str_slice(&self, range: TextRange) -> Result<Cow<'_, str>, TextError> {
337 let range = self.byte_range(range)?;
338 let v = self.text.byte_slice(range);
339 match v.as_str() {
340 Some(v) => Ok(Cow::Borrowed(v)),
341 None => Ok(Cow::Owned(v.to_string())),
342 }
343 }
344
345 fn str_slice_byte(&self, range: Range<usize>) -> Result<Cow<'_, str>, TextError> {
349 let Some(v) = self.text.get_byte_slice(range.clone()) else {
350 return Err(TextError::ByteRangeOutOfBounds(
351 Some(range.start),
352 Some(range.end),
353 self.text.len_bytes(),
354 ));
355 };
356 match v.as_str() {
357 Some(v) => Ok(Cow::Borrowed(v)),
358 None => Ok(Cow::Owned(v.to_string())),
359 }
360 }
361
362 fn graphemes(
367 &self,
368 range: TextRange,
369 pos: TextPosition,
370 ) -> Result<Self::GraphemeIter<'_>, TextError> {
371 if !range.contains_pos(pos) && range.end != pos {
372 return Err(TextError::TextPositionOutOfBounds(pos));
373 }
374
375 let range_bytes = self.byte_range(range)?;
376 let pos_byte = self.byte_range_at(pos)?.start;
377
378 let s = self
379 .text
380 .get_byte_slice(range_bytes.clone())
381 .expect("valid_range");
382
383 let r = RopeGraphemes::new_offset(range_bytes.start, s, pos_byte - range_bytes.start)
384 .expect("valid_bytes");
385
386 Ok(r)
387 }
388
389 fn graphemes_byte(
394 &self,
395 range: Range<usize>,
396 pos: usize,
397 ) -> Result<Self::GraphemeIter<'_>, TextError> {
398 if !range.contains(&pos) && range.end != pos {
399 return Err(TextError::ByteIndexOutOfBounds(pos, range.end));
400 }
401
402 let Some(s) = self.text.get_byte_slice(range.clone()) else {
403 return Err(TextError::ByteRangeInvalid(range.start, range.end));
404 };
405
406 let r = RopeGraphemes::new_offset(range.start, s, pos - range.start)?;
407
408 Ok(r)
409 }
410
411 fn line_at(&self, row: upos_type) -> Result<Cow<'_, str>, TextError> {
415 let len = self.len_lines() as upos_type;
416 if row < len {
417 if row < self.text.len_lines() as upos_type {
418 let v = self.text.get_line(row as usize).expect("valid_row");
419 match v.as_str() {
420 Some(v) => Ok(Cow::Borrowed(v)),
421 None => Ok(Cow::Owned(v.to_string())),
422 }
423 } else {
424 Ok(Cow::Borrowed(""))
425 }
426 } else {
427 Err(TextError::LineIndexOutOfBounds(row, len))
428 }
429 }
430
431 fn lines_at(&self, row: upos_type) -> Result<impl Iterator<Item = Cow<'_, str>>, TextError> {
435 let len = self.len_lines() as upos_type;
436 if row < len {
437 let it = self.text.get_lines_at(row as usize).expect("valid_row");
438 Ok(it.map(|v| match v.as_str() {
439 Some(v) => Cow::Borrowed(v),
440 None => Cow::Owned(v.to_string()),
441 }))
442 } else {
443 Err(TextError::LineIndexOutOfBounds(row, len))
444 }
445 }
446
447 #[inline]
452 fn line_graphemes(&self, row: upos_type) -> Result<Self::GraphemeIter<'_>, TextError> {
453 let row = self.normalize_row(row)?;
454 let line_byte = self.text.try_line_to_byte(row as usize)?;
455 let line = if row < self.text.len_lines() as upos_type {
456 self.text.get_line(row as usize).expect("valid_row")
457 } else {
458 RopeSlice::from("")
459 };
460 Ok(RopeGraphemes::new(line_byte, line))
461 }
462
463 #[inline]
468 fn line_width(&self, row: upos_type) -> Result<upos_type, TextError> {
469 let row = self.normalize_row(row)?;
470
471 if row < self.text.len_lines() as upos_type {
472 let r = self.text.get_line(row as usize).expect("valid_row");
473 let len = RopeGraphemes::new(0, r)
474 .filter(|g| !g.is_line_break())
475 .count() as upos_type;
476 Ok(len)
477 } else {
478 Ok(0)
479 }
480 }
481
482 #[inline]
483 #[allow(clippy::needless_bool)]
484 fn should_insert_newline(&self, pos: TextPosition) -> bool {
485 if pos.x == 0 && pos.y == 0 {
486 false
487 } else if pos.x == 0 && pos.y == self.len_lines() && !self.has_final_newline() {
488 true
489 } else if pos.x == 0 && pos.y == self.len_lines() - 1 && !self.has_final_newline() {
490 true
491 } else {
492 false
493 }
494 }
495
496 #[inline]
497 fn len_lines(&self) -> upos_type {
498 match self.text.len_bytes() {
499 0 => 1,
500 _ => {
501 let l = self.text.len_lines();
502 let t = if self.has_final_newline() { 0 } else { 1 };
503 (l + t) as upos_type
504 }
505 }
506 }
507
508 fn insert_char(
512 &mut self,
513 mut pos: TextPosition,
514 ch: char,
515 ) -> Result<(TextRange, Range<usize>), TextError> {
516 let pos_byte;
518 (pos, pos_byte) = self.normalize(pos)?;
519
520 self.invalidate(pos_byte);
522
523 let mut it_gr =
524 RopeGraphemes::new_offset(0, self.text.slice(..), pos_byte).expect("valid_bytes");
525 let prev = it_gr.prev();
526 it_gr.next();
527 let next = it_gr.next();
528
529 let insert_range = if ch == '\n' {
530 if let Some(prev) = prev {
531 if prev == "\r" {
532 TextRange::new(pos, pos)
533 } else {
534 TextRange::new(pos, (0, pos.y + 1))
535 }
536 } else {
537 TextRange::new(pos, (0, pos.y + 1))
538 }
539 } else if ch == '\r' {
540 if let Some(next) = next {
541 if next == "\n" {
542 TextRange::new(pos, pos)
543 } else {
544 TextRange::new(pos, (0, pos.y + 1))
545 }
546 } else {
547 TextRange::new(pos, (0, pos.y + 1))
548 }
549 } else if ch == '\u{000C}'
550 || ch == '\u{000B}'
551 || ch == '\u{0085}'
552 || ch == '\u{2028}'
553 || ch == '\u{2029}'
554 {
555 TextRange::new(pos, (0, pos.y + 1))
556 } else {
557 let mut len = 0;
559 self.buf.clear();
560 if let Some(prev) = prev {
561 len += 1;
562 self.buf.push_str(prev.grapheme());
563 }
564 len += 1;
565 self.buf.push(ch);
566 if let Some(next) = next {
567 len += 1;
568 self.buf.push_str(next.grapheme());
569 }
570 let buf_len = self.buf.graphemes(true).count();
571
572 let n = len - buf_len;
573
574 if n == 0 {
575 TextRange::new(pos, (pos.x + 1, pos.y))
576 } else if n == 1 {
577 TextRange::new(pos, pos)
579 } else if n == 2 {
580 TextRange::new(pos, pos)
582 } else {
583 unreachable!("insert_char {:?}", self.buf);
584 }
585 };
586
587 let pos_char = self.text.try_byte_to_char(pos_byte).expect("valid_bytes");
588
589 self.text
590 .try_insert_char(pos_char, ch)
591 .expect("valid_chars");
592
593 Ok((insert_range, pos_byte..pos_byte + ch.len_utf8()))
594 }
595
596 fn insert_str(
600 &mut self,
601 mut pos: TextPosition,
602 txt: &str,
603 ) -> Result<(TextRange, Range<usize>), TextError> {
604 let pos_byte;
606 (pos, pos_byte) = self.normalize(pos)?;
607
608 self.invalidate(pos_byte);
609
610 let pos_char = self.text.try_byte_to_char(pos_byte).expect("valid_bytes");
611
612 let mut line_count = 0;
613 let mut last_linebreak_idx = 0;
614 for c in StrGraphemes::new(0, txt) {
615 if c == "\r"
616 || c == "\n"
617 || c == "\r\n"
618 || c == "\u{000C}"
619 || c == "\u{000B}"
620 || c == "\u{0085}"
621 || c == "\u{2028}"
622 || c == "\u{2029}"
623 {
624 line_count += 1;
625 last_linebreak_idx = c.text_bytes().end;
626 }
627 }
628
629 let insert_range = if line_count > 0 {
630 self.buf.clear();
635 self.buf.push_str(&txt[last_linebreak_idx..]);
636 let old_offset = self.buf.len();
637
638 let line_offset = self
640 .text
641 .try_line_to_byte(pos.y as usize)
642 .expect("valid-pos");
643 let split = self .byte_range_at(pos)
645 .expect("valid_pos")
646 .start
647 - line_offset;
648 let remainder = self
649 .text
650 .get_line(pos.y as usize)
651 .expect("valid-pos")
652 .get_byte_slice(split..)
653 .expect("valid-pos");
654 for cc in remainder.chars() {
655 self.buf.push(cc);
656 }
657 let new_len = self.buf.graphemes(true).count() as upos_type;
658 let old_len = self.buf[old_offset..].graphemes(true).count() as upos_type;
659
660 self.text.try_insert(pos_char, txt).expect("valid_pos");
661
662 TextRange::new(pos, (new_len - old_len, pos.y + line_count))
663 } else {
664 let old_len = self.line_width(pos.y).expect("valid_line");
667 self.text.try_insert(pos_char, txt).expect("valid_pos");
668 let new_len = self.line_width(pos.y).expect("valid_line");
669
670 TextRange::new(pos, (pos.x + new_len - old_len, pos.y))
671 };
672
673 Ok((insert_range, pos_byte..pos_byte + txt.len()))
674 }
675
676 fn remove(
680 &mut self,
681 mut range: TextRange,
682 ) -> Result<(String, (TextRange, Range<usize>)), TextError> {
683 let start_byte_pos;
684 let end_byte_pos;
685
686 (range.start, start_byte_pos) = self.normalize(range.start)?;
687 (range.end, end_byte_pos) = self.normalize(range.end)?;
688
689 self.invalidate(start_byte_pos);
690
691 let old_text = self
692 .text
693 .get_byte_slice(start_byte_pos..end_byte_pos)
694 .expect("valid_bytes");
695 let old_text = old_text.to_string();
696
697 let start_pos = self
698 .text
699 .try_byte_to_char(start_byte_pos)
700 .expect("valid_bytes");
701 let end_pos = self
702 .text
703 .try_byte_to_char(end_byte_pos)
704 .expect("valid_bytes");
705
706 self.text.try_remove(start_pos..end_pos).expect("valid_pos");
707
708 Ok((old_text, (range, start_byte_pos..end_byte_pos)))
709 }
710
711 fn insert_b(&mut self, byte_pos: usize, t: &str) -> Result<(), TextError> {
716 let pos_char = self.text.try_byte_to_char(byte_pos)?;
717
718 self.invalidate(byte_pos);
719 self.text.try_insert(pos_char, t).expect("valid_pos");
720 Ok(())
721 }
722
723 fn remove_b(&mut self, byte_range: Range<usize>) -> Result<(), TextError> {
728 let start_char = self.text.try_byte_to_char(byte_range.start)?;
729 let end_char = self.text.try_byte_to_char(byte_range.end)?;
730
731 self.invalidate(byte_range.start);
732 self.text
733 .try_remove(start_char..end_char)
734 .expect("valid_range");
735 Ok(())
736 }
737}
738
739impl From<ropey::Error> for TextError {
740 fn from(err: ropey::Error) -> Self {
741 use ropey::Error;
742 match err {
743 Error::ByteIndexOutOfBounds(i, l) => TextError::ByteIndexOutOfBounds(i, l),
744 Error::CharIndexOutOfBounds(i, l) => TextError::CharIndexOutOfBounds(i, l),
745 Error::LineIndexOutOfBounds(i, l) => {
746 TextError::LineIndexOutOfBounds(i as upos_type, l as upos_type)
747 }
748 Error::Utf16IndexOutOfBounds(_, _) => {
749 unreachable!("{:?}", err)
750 }
751 Error::ByteIndexNotCharBoundary(i) => TextError::ByteIndexNotCharBoundary(i),
752 Error::ByteRangeNotCharBoundary(s, e) => TextError::ByteRangeNotCharBoundary(s, e),
753 Error::ByteRangeInvalid(s, e) => TextError::ByteRangeInvalid(s, e),
754 Error::CharRangeInvalid(s, e) => TextError::CharRangeInvalid(s, e),
755 Error::ByteRangeOutOfBounds(s, e, l) => TextError::ByteRangeOutOfBounds(s, e, l),
756 Error::CharRangeOutOfBounds(s, e, l) => TextError::CharRangeOutOfBounds(s, e, l),
757 _ => {
758 unreachable!("{:?}", err)
759 }
760 }
761 }
762}