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 cfg!(feature = "unicode_lines")
550 && (ch == '\u{000C}'
551 || ch == '\u{000B}'
552 || ch == '\u{0085}'
553 || ch == '\u{2028}'
554 || ch == '\u{2029}')
555 {
556 TextRange::new(pos, (0, pos.y + 1))
557 } else {
558 let mut len = 0;
560 self.buf.clear();
561 if let Some(prev) = prev {
562 len += 1;
563 self.buf.push_str(prev.grapheme());
564 }
565 len += 1;
566 self.buf.push(ch);
567 if let Some(next) = next {
568 len += 1;
569 self.buf.push_str(next.grapheme());
570 }
571 let buf_len = self.buf.graphemes(true).count();
572
573 let n = len - buf_len;
574
575 if n == 0 {
576 TextRange::new(pos, (pos.x + 1, pos.y))
577 } else if n == 1 {
578 TextRange::new(pos, pos)
580 } else if n == 2 {
581 TextRange::new(pos, pos)
583 } else {
584 unreachable!("insert_char {:?}", self.buf);
585 }
586 };
587
588 let pos_char = self.text.try_byte_to_char(pos_byte).expect("valid_bytes");
589
590 self.text
591 .try_insert_char(pos_char, ch)
592 .expect("valid_chars");
593
594 Ok((insert_range, pos_byte..pos_byte + ch.len_utf8()))
595 }
596
597 fn insert_str(
601 &mut self,
602 mut pos: TextPosition,
603 txt: &str,
604 ) -> Result<(TextRange, Range<usize>), TextError> {
605 let pos_byte;
607 (pos, pos_byte) = self.normalize(pos)?;
608
609 self.invalidate(pos_byte);
610
611 let pos_char = self.text.try_byte_to_char(pos_byte).expect("valid_bytes");
612
613 let mut line_count = 0;
614 let mut last_linebreak_idx = 0;
615 for c in StrGraphemes::new(0, txt) {
616 let test = if cfg!(feature = "cr_lines") {
617 c == "\r" || c == "\n" || c == "\r\n"
618 } else if cfg!(feature = "unicode_lines") {
619 c == "\r"
620 || c == "\n"
621 || c == "\r\n"
622 || c == "\u{000C}"
623 || c == "\u{000B}"
624 || c == "\u{0085}"
625 || c == "\u{2028}"
626 || c == "\u{2029}"
627 } else {
628 c == "\n" || c == "\r\n"
629 };
630
631 if test {
632 line_count += 1;
633 last_linebreak_idx = c.text_bytes().end;
634 }
635 }
636
637 let insert_range = if line_count > 0 {
638 self.buf.clear();
643 self.buf.push_str(&txt[last_linebreak_idx..]);
644 let old_offset = self.buf.len();
645
646 let line_offset = self
648 .text
649 .try_line_to_byte(pos.y as usize)
650 .expect("valid-pos");
651 let split = self .byte_range_at(pos)
653 .expect("valid_pos")
654 .start
655 - line_offset;
656 let remainder = self
657 .text
658 .get_line(pos.y as usize)
659 .expect("valid-pos")
660 .get_byte_slice(split..)
661 .expect("valid-pos");
662 for cc in remainder.chars() {
663 self.buf.push(cc);
664 }
665 let new_len = self.buf.graphemes(true).count() as upos_type;
666 let old_len = self.buf[old_offset..].graphemes(true).count() as upos_type;
667
668 self.text.try_insert(pos_char, txt).expect("valid_pos");
669
670 TextRange::new(pos, (new_len - old_len, pos.y + line_count))
671 } else {
672 let old_len = self.line_width(pos.y).expect("valid_line");
675 self.text.try_insert(pos_char, txt).expect("valid_pos");
676 let new_len = self.line_width(pos.y).expect("valid_line");
677
678 TextRange::new(pos, (pos.x + new_len - old_len, pos.y))
679 };
680
681 Ok((insert_range, pos_byte..pos_byte + txt.len()))
682 }
683
684 fn remove(
688 &mut self,
689 mut range: TextRange,
690 ) -> Result<(String, (TextRange, Range<usize>)), TextError> {
691 let start_byte_pos;
692 let end_byte_pos;
693
694 (range.start, start_byte_pos) = self.normalize(range.start)?;
695 (range.end, end_byte_pos) = self.normalize(range.end)?;
696
697 self.invalidate(start_byte_pos);
698
699 let old_text = self
700 .text
701 .get_byte_slice(start_byte_pos..end_byte_pos)
702 .expect("valid_bytes");
703 let old_text = old_text.to_string();
704
705 let start_pos = self
706 .text
707 .try_byte_to_char(start_byte_pos)
708 .expect("valid_bytes");
709 let end_pos = self
710 .text
711 .try_byte_to_char(end_byte_pos)
712 .expect("valid_bytes");
713
714 self.text.try_remove(start_pos..end_pos).expect("valid_pos");
715
716 Ok((old_text, (range, start_byte_pos..end_byte_pos)))
717 }
718
719 fn insert_b(&mut self, byte_pos: usize, t: &str) -> Result<(), TextError> {
724 let pos_char = self.text.try_byte_to_char(byte_pos)?;
725
726 self.invalidate(byte_pos);
727 self.text.try_insert(pos_char, t).expect("valid_pos");
728 Ok(())
729 }
730
731 fn remove_b(&mut self, byte_range: Range<usize>) -> Result<(), TextError> {
736 let start_char = self.text.try_byte_to_char(byte_range.start)?;
737 let end_char = self.text.try_byte_to_char(byte_range.end)?;
738
739 self.invalidate(byte_range.start);
740 self.text
741 .try_remove(start_char..end_char)
742 .expect("valid_range");
743 Ok(())
744 }
745}
746
747impl From<ropey::Error> for TextError {
748 fn from(err: ropey::Error) -> Self {
749 use ropey::Error;
750 match err {
751 Error::ByteIndexOutOfBounds(i, l) => TextError::ByteIndexOutOfBounds(i, l),
752 Error::CharIndexOutOfBounds(i, l) => TextError::CharIndexOutOfBounds(i, l),
753 Error::LineIndexOutOfBounds(i, l) => {
754 TextError::LineIndexOutOfBounds(i as upos_type, l as upos_type)
755 }
756 Error::Utf16IndexOutOfBounds(_, _) => {
757 unreachable!("{:?}", err)
758 }
759 Error::ByteIndexNotCharBoundary(i) => TextError::ByteIndexNotCharBoundary(i),
760 Error::ByteRangeNotCharBoundary(s, e) => TextError::ByteRangeNotCharBoundary(s, e),
761 Error::ByteRangeInvalid(s, e) => TextError::ByteRangeInvalid(s, e),
762 Error::CharRangeInvalid(s, e) => TextError::CharRangeInvalid(s, e),
763 Error::ByteRangeOutOfBounds(s, e, l) => TextError::ByteRangeOutOfBounds(s, e, l),
764 Error::CharRangeOutOfBounds(s, e, l) => TextError::CharRangeOutOfBounds(s, e, l),
765 _ => {
766 unreachable!("{:?}", err)
767 }
768 }
769 }
770}