rat_text/
text_store.rs

1use crate::grapheme::Grapheme;
2use crate::{upos_type, Cursor, TextError, TextPosition, TextRange};
3use std::borrow::Cow;
4use std::ops::Range;
5
6/// Backing store for the TextCore.
7pub trait TextStore {
8    /// Can store multi-line content?
9    fn is_multi_line(&self) -> bool;
10
11    /// Get content as string.
12    fn string(&self) -> String;
13
14    /// Set content from string.
15    fn set_string(&mut self, t: &str);
16
17    /// Grapheme position to byte position.
18    /// This is the (start,end) position of the single grapheme after pos.
19    ///
20    /// * pos must be a valid position: row <= len_lines, col <= line_width of the row.
21    fn byte_range_at(&self, pos: TextPosition) -> Result<Range<usize>, TextError>;
22
23    /// Grapheme range to byte range.
24    ///
25    /// * range must be a valid range. row <= len_lines, col <= line_width of the row.
26    fn byte_range(&self, range: TextRange) -> Result<Range<usize>, TextError>;
27
28    /// Byte position to grapheme position.
29    /// Returns the position that contains the given byte index.
30    ///
31    /// * byte must <= byte-len.
32    fn byte_to_pos(&self, byte: usize) -> Result<TextPosition, TextError>;
33
34    /// Byte range to grapheme range.
35    ///
36    /// * byte must <= byte-len.
37    fn bytes_to_range(&self, bytes: Range<usize>) -> Result<TextRange, TextError>;
38
39    /// A range of the text as `Cow<str>`.
40    ///
41    /// * range must be a valid range. row <= len_lines, col <= line_width of the row.
42    /// * pos must be inside of range.
43    fn str_slice(&self, range: TextRange) -> Result<Cow<'_, str>, TextError>;
44
45    /// A range of the text as `Cow<str>`.
46    ///
47    /// * range must be valid
48    fn str_slice_byte(&self, range: Range<usize>) -> Result<Cow<'_, str>, TextError>;
49
50    /// Return a cursor over the graphemes of the range, start at the given position.
51    ///
52    /// * range must be a valid range. row <= len_lines, col <= line_width of the row.
53    /// * pos must be inside of range.
54    fn graphemes(
55        &self,
56        range: TextRange,
57        pos: TextPosition,
58    ) -> Result<impl Cursor<Item = Grapheme<'_>>, TextError>;
59
60    /// Line as str.
61    ///
62    /// * row must be <= len_lines
63    fn line_at(&self, row: upos_type) -> Result<Cow<'_, str>, TextError>;
64
65    /// Iterate over text-lines, starting at line-offset.
66    ///
67    /// * row must be <= len_lines
68    fn lines_at(&self, row: upos_type) -> Result<impl Iterator<Item = Cow<'_, str>>, TextError>;
69
70    /// Return a line as an iterator over the graphemes.
71    /// This contains the '\n' at the end.
72    ///
73    /// * row must be <= len_lines
74    fn line_graphemes(&self, row: upos_type)
75        -> Result<impl Cursor<Item = Grapheme<'_>>, TextError>;
76
77    /// Line width of row as grapheme count.
78    /// Excludes the terminating '\n'.
79    ///
80    /// * row must be <= len_lines
81    fn line_width(&self, row: upos_type) -> Result<upos_type, TextError>;
82
83    /// Number of lines.
84    fn len_lines(&self) -> upos_type;
85
86    /// Insert a char at the given position.
87    ///
88    /// * range must be a valid range. row <= len_lines, col <= line_width of the row.
89    fn insert_char(
90        &mut self,
91        pos: TextPosition,
92        c: char,
93    ) -> Result<(TextRange, Range<usize>), TextError>;
94
95    /// Insert a text str at the given position.
96    ///
97    /// * range must be a valid range. row <= len_lines, col <= line_width of the row.
98    fn insert_str(
99        &mut self,
100        pos: TextPosition,
101        t: &str,
102    ) -> Result<(TextRange, Range<usize>), TextError>;
103
104    /// Remove the given text range.
105    ///
106    /// * range must be a valid range. row <= len_lines, col <= line_width of the row.
107    fn remove(
108        &mut self,
109        range: TextRange,
110    ) -> Result<(String, (TextRange, Range<usize>)), TextError>;
111
112    /// Insert a string at the given byte index.
113    /// Call this only for undo.
114    ///
115    /// byte_pos must be <= len bytes.
116    fn insert_b(&mut self, byte_pos: usize, t: &str) -> Result<(), TextError>;
117
118    /// Remove the given byte-range.
119    /// Call this only for undo.
120    ///
121    /// byte_pos must be <= len bytes.
122    fn remove_b(&mut self, byte_range: Range<usize>) -> Result<(), TextError>;
123}
124
125pub(crate) mod text_rope {
126    use crate::grapheme::{Grapheme, RopeGraphemes};
127    use crate::text_store::{Cursor, TextStore};
128    use crate::{upos_type, TextError, TextPosition, TextRange};
129    use ropey::{Rope, RopeSlice};
130    use std::borrow::Cow;
131    use std::mem;
132    use std::ops::Range;
133    use unicode_segmentation::UnicodeSegmentation;
134
135    /// Text store with a rope.
136    #[derive(Debug, Clone, Default)]
137    pub struct TextRope {
138        text: Rope,
139        // tmp buf
140        buf: String,
141    }
142
143    /// Length as grapheme count, excluding line breaks.
144    #[inline]
145    fn rope_line_len(r: RopeSlice<'_>) -> upos_type {
146        let it = RopeGraphemes::new(0, r);
147        it.filter(|g| !g.is_line_break()).count() as upos_type
148    }
149
150    /// Length as grapheme count, excluding line breaks.
151    #[inline]
152    fn str_line_len(s: &str) -> upos_type {
153        let it = s.graphemes(true);
154        it.filter(|c| *c != "\n" && *c != "\r\n").count() as upos_type
155    }
156
157    impl TextRope {
158        /// Returns the first char position for the grapheme position.
159        #[inline]
160        fn char_at(&self, pos: TextPosition) -> Result<usize, TextError> {
161            let byte_range = self.byte_range_at(pos)?;
162            Ok(self
163                .text
164                .try_byte_to_char(byte_range.start)
165                .expect("valid_bytes"))
166        }
167
168        /// Iterator for the chars of a given line.
169        #[inline]
170        fn line_chars(&self, row: upos_type) -> Result<impl Iterator<Item = char> + '_, TextError> {
171            let Some(line) = self.text.get_line(row as usize) else {
172                return Err(TextError::LineIndexOutOfBounds(
173                    row,
174                    self.text.len_lines() as upos_type,
175                ));
176            };
177            Ok(line.chars())
178        }
179    }
180
181    impl TextRope {
182        /// New empty.
183        pub fn new() -> Self {
184            Self::default()
185        }
186
187        /// New from string.
188        pub fn new_text(t: &str) -> Self {
189            Self {
190                text: Rope::from_str(t),
191                buf: Default::default(),
192            }
193        }
194
195        /// New from rope.
196        pub fn new_rope(r: Rope) -> Self {
197            Self {
198                text: r,
199                buf: Default::default(),
200            }
201        }
202
203        /// Borrow the rope
204        pub fn rope(&self) -> &Rope {
205            &self.text
206        }
207
208        /// A range of the text as RopeSlice.
209        #[inline]
210        pub fn rope_slice(&self, range: TextRange) -> Result<RopeSlice<'_>, TextError> {
211            let s = self.char_at(range.start)?;
212            let e = self.char_at(range.end)?;
213            Ok(self.text.get_slice(s..e).expect("valid_range"))
214        }
215    }
216
217    impl TextStore for TextRope {
218        /// Can store multi-line content?
219        ///
220        /// If this returns false it is an error to call any function with
221        /// a row other than `0`.
222        fn is_multi_line(&self) -> bool {
223            true
224        }
225
226        /// Content as string.
227        fn string(&self) -> String {
228            self.text.to_string()
229        }
230
231        /// Set content.
232        fn set_string(&mut self, t: &str) {
233            self.text = Rope::from_str(t);
234        }
235
236        /// Grapheme position to byte position.
237        /// This is the (start,end) position of the single grapheme after pos.
238        ///
239        /// * pos must be a valid position: row <= len_lines, col <= line_width of the row.
240        fn byte_range_at(&self, pos: TextPosition) -> Result<Range<usize>, TextError> {
241            let it_line = self.line_graphemes(pos.y)?;
242
243            let mut col = 0;
244            let mut byte_end = it_line.text_offset();
245            for grapheme in it_line {
246                if col == pos.x {
247                    return Ok(grapheme.text_bytes());
248                }
249                col += 1;
250                byte_end = grapheme.text_bytes().end;
251            }
252            // one past the end is ok.
253            if col == pos.x {
254                Ok(byte_end..byte_end)
255            } else {
256                Err(TextError::ColumnIndexOutOfBounds(pos.x, col))
257            }
258        }
259
260        /// Grapheme range to byte range.
261        ///
262        /// * range must be a valid range. row <= len_lines, col <= line_width of the row.
263        fn byte_range(&self, range: TextRange) -> Result<Range<usize>, TextError> {
264            if range.start.y == range.end.y {
265                let it_line = self.line_graphemes(range.start.y)?;
266
267                let mut range_start = None;
268                let mut range_end = None;
269                let mut col = 0;
270                let mut byte_end = it_line.text_offset();
271                for grapheme in it_line {
272                    if col == range.start.x {
273                        range_start = Some(grapheme.text_bytes().start);
274                    }
275                    if col == range.end.x {
276                        range_end = Some(grapheme.text_bytes().end);
277                    }
278                    if range_start.is_some() && range_end.is_some() {
279                        break;
280                    }
281                    col += 1;
282                    byte_end = grapheme.text_bytes().end;
283                }
284                // one past the end is ok.
285                if col == range.start.x {
286                    range_start = Some(byte_end);
287                }
288                if col == range.end.x {
289                    range_end = Some(byte_end);
290                }
291
292                let Some(range_start) = range_start else {
293                    return Err(TextError::ColumnIndexOutOfBounds(range.start.x, col));
294                };
295                let Some(range_end) = range_end else {
296                    return Err(TextError::ColumnIndexOutOfBounds(range.end.x, col));
297                };
298
299                Ok(range_start..range_end)
300            } else {
301                let range_start = self.byte_range_at(range.start)?;
302                let range_end = self.byte_range_at(range.end)?;
303
304                Ok(range_start.start..range_end.start)
305            }
306        }
307
308        /// Byte position to grapheme position.
309        /// Returns the position that contains the given byte index.
310        ///
311        /// * byte must <= byte-len.
312        fn byte_to_pos(&self, byte_pos: usize) -> Result<TextPosition, TextError> {
313            let Ok(row) = self.text.try_byte_to_line(byte_pos) else {
314                return Err(TextError::ByteIndexOutOfBounds(
315                    byte_pos,
316                    self.text.len_bytes(),
317                ));
318            };
319            let row = row as upos_type;
320
321            let mut col = 0;
322            let it_line = self.line_graphemes(row)?;
323            for grapheme in it_line {
324                if byte_pos < grapheme.text_bytes().end {
325                    break;
326                }
327                col += 1;
328            }
329
330            Ok(TextPosition::new(col, row))
331        }
332
333        /// Byte range to grapheme range.
334        ///
335        /// * byte must <= byte-len.
336        fn bytes_to_range(&self, bytes: Range<usize>) -> Result<TextRange, TextError> {
337            let Ok(start_row) = self.text.try_byte_to_line(bytes.start) else {
338                return Err(TextError::ByteIndexOutOfBounds(
339                    bytes.start,
340                    self.text.len_bytes(),
341                ));
342            };
343            let start_row = start_row as upos_type;
344            let Ok(end_row) = self.text.try_byte_to_line(bytes.end) else {
345                return Err(TextError::ByteIndexOutOfBounds(
346                    bytes.end,
347                    self.text.len_bytes(),
348                ));
349            };
350            let end_row = end_row as upos_type;
351
352            if start_row == end_row {
353                let mut col = 0;
354                let mut start = None;
355                let mut end = None;
356                let it_line = self.line_graphemes(start_row)?;
357                for grapheme in it_line {
358                    if bytes.start < grapheme.text_bytes().end {
359                        if start.is_none() {
360                            start = Some(col);
361                        }
362                    }
363                    if bytes.end < grapheme.text_bytes().end {
364                        if end.is_none() {
365                            end = Some(col);
366                        }
367                    }
368                    if start.is_some() && end.is_some() {
369                        break;
370                    }
371                    col += 1;
372                }
373                if bytes.start == self.text.len_bytes() {
374                    start = Some(col);
375                }
376                if bytes.end == self.text.len_bytes() {
377                    end = Some(col);
378                }
379
380                let Some(start) = start else {
381                    return Err(TextError::ByteIndexOutOfBounds(
382                        bytes.start,
383                        self.text.len_bytes(),
384                    ));
385                };
386                let Some(end) = end else {
387                    return Err(TextError::ByteIndexOutOfBounds(
388                        bytes.end,
389                        self.text.len_bytes(),
390                    ));
391                };
392
393                Ok(TextRange::new((start, start_row), (end, end_row)))
394            } else {
395                let start = self.byte_to_pos(bytes.start)?;
396                let end = self.byte_to_pos(bytes.end)?;
397
398                Ok(TextRange::new(start, end))
399            }
400        }
401
402        /// A range of the text as `Cow<str>`.
403        ///
404        /// * range must be a valid range. row <= len_lines, col <= line_width of the row.
405        /// * pos must be inside of range.
406        fn str_slice(&self, range: TextRange) -> Result<Cow<'_, str>, TextError> {
407            let start_char = self.char_at(range.start)?;
408            let end_char = self.char_at(range.end)?;
409            let v = self
410                .text
411                .get_slice(start_char..end_char)
412                .expect("valid_slice");
413            match v.as_str() {
414                Some(v) => Ok(Cow::Borrowed(v)),
415                None => Ok(Cow::Owned(v.to_string())),
416            }
417        }
418
419        /// A range of the text as `Cow<str>`.
420        ///
421        /// The byte-range must be a valid range.
422        fn str_slice_byte(&self, range: Range<usize>) -> Result<Cow<'_, str>, TextError> {
423            let Some(v) = self.text.get_byte_slice(range.clone()) else {
424                return Err(TextError::ByteRangeOutOfBounds(
425                    Some(range.start),
426                    Some(range.end),
427                    self.text.len_bytes(),
428                ));
429            };
430            match v.as_str() {
431                Some(v) => Ok(Cow::Borrowed(v)),
432                None => Ok(Cow::Owned(v.to_string())),
433            }
434        }
435
436        /// Return a cursor over the graphemes of the range, start at the given position.
437        ///
438        /// * range must be a valid range. row <= len_lines, col <= line_width of the row.
439        /// * pos must be inside of range.
440        fn graphemes(
441            &self,
442            range: TextRange,
443            pos: TextPosition,
444        ) -> Result<impl Cursor<Item = Grapheme<'_>>, TextError> {
445            if !range.contains_pos(pos) && range.end != pos {
446                return Err(TextError::TextPositionOutOfBounds(pos));
447            }
448
449            let range_bytes = self.byte_range(range)?;
450            let pos_byte = self.byte_range_at(pos)?.start;
451
452            let s = self
453                .text
454                .get_byte_slice(range_bytes.clone())
455                .expect("valid_range");
456
457            Ok(
458                RopeGraphemes::new_offset(range_bytes.start, s, pos_byte - range_bytes.start)
459                    .expect("valid_bytes"),
460            )
461        }
462
463        /// Line as str.
464        ///
465        /// * row must be <= len_lines
466        fn line_at(&self, row: upos_type) -> Result<Cow<'_, str>, TextError> {
467            let len = self.text.len_lines() as upos_type;
468            if row > len {
469                Err(TextError::LineIndexOutOfBounds(row, len))
470            } else if row == len {
471                Ok(Cow::Borrowed(""))
472            } else {
473                let v = self.text.get_line(row as usize).expect("valid_row");
474                match v.as_str() {
475                    Some(v) => Ok(Cow::Borrowed(v)),
476                    None => Ok(Cow::Owned(v.to_string())),
477                }
478            }
479        }
480
481        /// Iterate over text-lines, starting at line-offset.
482        ///
483        /// * row must be <= len_lines
484        fn lines_at(
485            &self,
486            row: upos_type,
487        ) -> Result<impl Iterator<Item = Cow<'_, str>>, TextError> {
488            let len = self.text.len_lines() as upos_type;
489            if row > len {
490                Err(TextError::LineIndexOutOfBounds(row, len))
491            } else {
492                let it = self.text.get_lines_at(row as usize).expect("valid_row");
493                Ok(it.map(|v| match v.as_str() {
494                    Some(v) => Cow::Borrowed(v),
495                    None => Cow::Owned(v.to_string()),
496                }))
497            }
498        }
499
500        /// Return a line as an iterator over the graphemes.
501        /// This contains the '\n' at the end.
502        ///
503        /// * row must be <= len_lines
504        #[inline]
505        fn line_graphemes(
506            &self,
507            row: upos_type,
508        ) -> Result<impl Cursor<Item = Grapheme<'_>>, TextError> {
509            let line_byte = self.text.try_line_to_byte(row as usize)?;
510            // try_line_to_byte and get_line don't have the same boundaries.
511            // the former accepts one past the end, the latter doesn't.
512            // here we need the first behaviour.
513            if let Some(line) = self.text.get_line(row as usize) {
514                Ok(RopeGraphemes::new(line_byte, line))
515            } else {
516                Ok(RopeGraphemes::new(line_byte, RopeSlice::from("")))
517            }
518        }
519
520        /// Line width as grapheme count.
521        /// Excludes the terminating '\n'.
522        ///
523        /// * row must be <= len_lines
524        #[inline]
525        fn line_width(&self, row: upos_type) -> Result<upos_type, TextError> {
526            let len = self.text.len_lines() as upos_type;
527            if row > len {
528                Err(TextError::LineIndexOutOfBounds(row, len))
529            } else if row == len {
530                Ok(0)
531            } else {
532                let v = self.text.get_line(row as usize).expect("valid_row");
533                Ok(rope_line_len(v))
534            }
535        }
536
537        fn len_lines(&self) -> upos_type {
538            self.text.len_lines() as upos_type
539        }
540
541        /// Insert a char at the given position.
542        ///
543        /// * range must be a valid range. row <= len_lines, col <= line_width of the row.
544        fn insert_char(
545            &mut self,
546            pos: TextPosition,
547            ch: char,
548        ) -> Result<(TextRange, Range<usize>), TextError> {
549            let pos_byte = self.byte_range_at(pos)?;
550            let pos_char = self
551                .text
552                .try_byte_to_char(pos_byte.start)
553                .expect("valid_bytes");
554
555            let mut it_gr = RopeGraphemes::new_offset(0, self.text.slice(..), pos_byte.start)
556                .expect("valid_bytes");
557
558            let prev = it_gr.prev();
559            it_gr.next();
560            let next = it_gr.next();
561
562            let insert_range = if ch == '\n' {
563                if let Some(prev) = prev {
564                    if prev == "\r" {
565                        TextRange::new(pos, pos)
566                    } else {
567                        TextRange::new(pos, (0, pos.y + 1))
568                    }
569                } else {
570                    TextRange::new(pos, (0, pos.y + 1))
571                }
572            } else if ch == '\r' {
573                if let Some(next) = next {
574                    if next == "\n" {
575                        TextRange::new(pos, pos)
576                    } else {
577                        TextRange::new(pos, (0, pos.y + 1))
578                    }
579                } else {
580                    TextRange::new(pos, (0, pos.y + 1))
581                }
582            } else {
583                let mut len = 0;
584                self.buf.clear();
585                if let Some(prev) = prev {
586                    len += 1;
587                    self.buf.push_str(prev.grapheme());
588                }
589                len += 1;
590                self.buf.push(ch);
591                if let Some(next) = next {
592                    len += 1;
593                    self.buf.push_str(next.grapheme());
594                }
595
596                let n = len - self.buf.graphemes(true).count();
597                if n == 0 {
598                    TextRange::new(pos, (pos.x + 1, pos.y))
599                } else if n == 1 {
600                    // combined some
601                    TextRange::new(pos, pos)
602                } else if n == 2 {
603                    // combined some
604                    TextRange::new(pos, pos)
605                } else {
606                    unreachable!("insert_char {:?}", self.buf);
607                }
608            };
609
610            self.text
611                .try_insert_char(pos_char, ch)
612                .expect("valid_chars");
613
614            Ok((insert_range, pos_byte.start..pos_byte.start + ch.len_utf8()))
615        }
616
617        /// Insert a text str at the given position.
618        ///
619        /// * range must be a valid range. row <= len_lines, col <= line_width of the row.
620        fn insert_str(
621            &mut self,
622            pos: TextPosition,
623            txt: &str,
624        ) -> Result<(TextRange, Range<usize>), TextError> {
625            let pos_byte = self.byte_range_at(pos)?;
626            let pos_char = self
627                .text
628                .try_byte_to_char(pos_byte.start)
629                .expect("valid_bytes");
630
631            let mut line_count = 0;
632            let mut last_linebreak_idx = 0;
633            for (p, c) in txt.char_indices() {
634                if c == '\n' {
635                    line_count += 1;
636                    last_linebreak_idx = p + 1;
637                }
638            }
639
640            let insert_range = if line_count > 0 {
641                let mut buf = mem::take(&mut self.buf);
642
643                // Find the length of line after the insert position.
644                let split = self.char_at(pos).expect("valid_pos");
645                let line = self.line_chars(pos.y).expect("valid_pos");
646                buf.clear();
647                for c in line.skip(split) {
648                    buf.push(c);
649                }
650                let old_len = str_line_len(&buf);
651                buf.clear();
652
653                // compose the new line and find its length.
654                buf.push_str(&txt[last_linebreak_idx..]);
655                let line = self.line_chars(pos.y).expect("valid_pos");
656                for c in line.skip(split) {
657                    buf.push(c);
658                }
659                let new_len = str_line_len(&buf);
660                buf.clear();
661                self.buf = buf;
662
663                self.text.try_insert(pos_char, txt).expect("valid_pos");
664
665                TextRange::new(pos, (new_len - old_len, pos.y + line_count))
666            } else {
667                // no way to know if the insert text combines with a surrounding char.
668                // the difference of the graphem len seems safe though.
669                let old_len = self.line_width(pos.y).expect("valid_line");
670
671                self.text.try_insert(pos_char, txt).expect("valid_pos");
672
673                let new_len = self.line_width(pos.y).expect("valid_line");
674
675                TextRange::new(pos, (pos.x + new_len - old_len, pos.y))
676            };
677
678            Ok((insert_range, pos_byte.start..pos_byte.start + txt.len()))
679        }
680
681        /// Remove the given text range.
682        ///
683        /// * range must be a valid range. row <= len_lines, col <= line_width of the row.
684        fn remove(
685            &mut self,
686            range: TextRange,
687        ) -> Result<(String, (TextRange, Range<usize>)), TextError> {
688            let start_byte_pos = self.byte_range_at(range.start)?;
689            let end_byte_pos = self.byte_range_at(range.end)?;
690
691            let start_pos = self
692                .text
693                .try_byte_to_char(start_byte_pos.start)
694                .expect("valid_bytes");
695            let end_pos = self
696                .text
697                .try_byte_to_char(end_byte_pos.start)
698                .expect("valid_bytes");
699
700            let old_text = self
701                .text
702                .get_slice(start_pos..end_pos)
703                .expect("valid_bytes");
704            let old_text = old_text.to_string();
705
706            self.text.try_remove(start_pos..end_pos).expect("valid_pos");
707
708            Ok((old_text, (range, start_byte_pos.start..end_byte_pos.start)))
709        }
710
711        /// Insert a string at the given byte index.
712        /// Call this only for undo.
713        ///
714        /// byte_pos must be <= len bytes.
715        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            self.text.try_insert(pos_char, t).expect("valid_pos");
718            Ok(())
719        }
720
721        /// Remove the given byte-range.
722        /// Call this only for undo.
723        ///
724        /// byte_pos must be <= len bytes.
725        fn remove_b(&mut self, byte_range: Range<usize>) -> Result<(), TextError> {
726            let start_char = self.text.try_byte_to_char(byte_range.start)?;
727            let end_char = self.text.try_byte_to_char(byte_range.end)?;
728            self.text
729                .try_remove(start_char..end_char)
730                .expect("valid_range");
731            Ok(())
732        }
733    }
734
735    impl From<ropey::Error> for TextError {
736        fn from(err: ropey::Error) -> Self {
737            use ropey::Error;
738            match err {
739                Error::ByteIndexOutOfBounds(i, l) => TextError::ByteIndexOutOfBounds(i, l),
740                Error::CharIndexOutOfBounds(i, l) => TextError::CharIndexOutOfBounds(i, l),
741                Error::LineIndexOutOfBounds(i, l) => {
742                    TextError::LineIndexOutOfBounds(i as upos_type, l as upos_type)
743                }
744                Error::Utf16IndexOutOfBounds(_, _) => {
745                    unreachable!("{:?}", err)
746                }
747                Error::ByteIndexNotCharBoundary(i) => TextError::ByteIndexNotCharBoundary(i),
748                Error::ByteRangeNotCharBoundary(s, e) => TextError::ByteRangeNotCharBoundary(s, e),
749                Error::ByteRangeInvalid(s, e) => TextError::ByteRangeInvalid(s, e),
750                Error::CharRangeInvalid(s, e) => TextError::CharRangeInvalid(s, e),
751                Error::ByteRangeOutOfBounds(s, e, l) => TextError::ByteRangeOutOfBounds(s, e, l),
752                Error::CharRangeOutOfBounds(s, e, l) => TextError::CharRangeOutOfBounds(s, e, l),
753                _ => {
754                    unreachable!("{:?}", err)
755                }
756            }
757        }
758    }
759}
760
761pub(crate) mod text_string {
762    use crate::grapheme::{Grapheme, StrGraphemes};
763    use crate::text_store::{Cursor, TextStore};
764    use crate::{upos_type, TextError, TextPosition, TextRange};
765    use std::borrow::Cow;
766    use std::iter::once;
767    use std::mem;
768    use std::ops::Range;
769    use unicode_segmentation::UnicodeSegmentation;
770
771    /// Single line text-store.
772    #[derive(Debug, Default, Clone)]
773    pub struct TextString {
774        // text
775        text: String,
776        // len as grapheme count
777        len: upos_type,
778        // tmp buffer
779        buf: String,
780    }
781
782    /// Length as grapheme count, excluding line breaks.
783    #[inline]
784    fn str_len(s: &str) -> upos_type {
785        s.graphemes(true).count() as upos_type
786    }
787
788    impl TextString {
789        /// New empty.
790        pub fn new() -> Self {
791            Self {
792                text: Default::default(),
793                len: 0,
794                buf: Default::default(),
795            }
796        }
797
798        /// New from string.
799        pub fn new_text(t: &str) -> Self {
800            Self {
801                text: t.into(),
802                len: str_len(t),
803                buf: Default::default(),
804            }
805        }
806
807        /// New from string.
808        pub fn new_string(t: String) -> Self {
809            let len = str_len(&t);
810            Self {
811                text: t,
812                len,
813                buf: Default::default(),
814            }
815        }
816
817        /// str
818        pub fn as_str(&self) -> &str {
819            self.text.as_str()
820        }
821    }
822
823    impl TextStore for TextString {
824        /// Can store multi-line content?
825        ///
826        /// todo: allow col=0, row=1
827        fn is_multi_line(&self) -> bool {
828            false
829        }
830
831        /// Get content as string.
832        fn string(&self) -> String {
833            self.text.to_string()
834        }
835
836        /// Set content as string.
837        fn set_string(&mut self, t: &str) {
838            self.text = t.to_string();
839            self.len = str_len(&self.text);
840        }
841
842        /// Grapheme position to byte position.
843        /// This is the (start,end) position of the single grapheme after pos.
844        ///
845        /// * pos must be a valid position: row <= len_lines, col <= line_width of the row.
846        fn byte_range_at(&self, pos: TextPosition) -> Result<Range<usize>, TextError> {
847            if pos.y != 0 && pos != TextPosition::new(0, 1) {
848                return Err(TextError::LineIndexOutOfBounds(pos.y, 1));
849            };
850
851            if pos == TextPosition::new(0, 1) {
852                let len = self.text.len();
853                return Ok(len..len);
854            }
855
856            let mut byte_range = None;
857            for (cidx, (idx, c)) in self
858                .text
859                .grapheme_indices(true)
860                .chain(once((self.text.len(), "")))
861                .enumerate()
862            {
863                if cidx == pos.x as usize {
864                    byte_range = Some(idx..idx + c.len());
865                    break;
866                }
867            }
868
869            if let Some(byte_range) = byte_range {
870                Ok(byte_range)
871            } else {
872                Err(TextError::ColumnIndexOutOfBounds(
873                    pos.x,
874                    str_len(&self.text),
875                ))
876            }
877        }
878
879        /// Grapheme range to byte range.
880        ///
881        /// * range must be a valid range. row <= len_lines, col <= line_width of the row.
882        fn byte_range(&self, range: TextRange) -> Result<Range<usize>, TextError> {
883            if range.start.y != 0 && range.start != TextPosition::new(0, 1) {
884                return Err(TextError::LineIndexOutOfBounds(range.start.y, 1));
885            };
886            if range.end.y != 0 && range.end != TextPosition::new(0, 1) {
887                return Err(TextError::LineIndexOutOfBounds(range.end.y, 1));
888            };
889
890            let mut byte_start = None;
891            let mut byte_end = None;
892
893            if range.start == TextPosition::new(0, 1) {
894                byte_start = Some(self.text.len());
895            }
896            if range.end == TextPosition::new(0, 1) {
897                byte_end = Some(self.text.len());
898            }
899
900            if byte_start.is_none() || byte_end.is_none() {
901                for (cidx, (idx, _)) in self
902                    .text
903                    .grapheme_indices(true)
904                    .chain(once((self.text.len(), "")))
905                    .enumerate()
906                {
907                    if TextPosition::new(cidx as upos_type, 0) == range.start {
908                        byte_start = Some(idx);
909                    }
910                    if TextPosition::new(cidx as upos_type, 0) == range.end {
911                        byte_end = Some(idx);
912                    }
913                    if byte_start.is_some() && byte_end.is_some() {
914                        break;
915                    }
916                }
917            }
918
919            let Some(byte_start) = byte_start else {
920                return Err(TextError::ColumnIndexOutOfBounds(
921                    range.start.x,
922                    str_len(&self.text),
923                ));
924            };
925            let Some(byte_end) = byte_end else {
926                return Err(TextError::ColumnIndexOutOfBounds(
927                    range.end.x,
928                    str_len(&self.text),
929                ));
930            };
931
932            Ok(byte_start..byte_end)
933        }
934
935        /// Byte position to grapheme position.
936        /// Returns the position that contains the given byte index.
937        ///
938        /// * byte must <= byte-len.
939        fn byte_to_pos(&self, byte_pos: usize) -> Result<TextPosition, TextError> {
940            let mut pos = None;
941
942            for (cidx, (c_start, c)) in self
943                .text
944                .grapheme_indices(true)
945                .chain(once((self.text.len(), " ")))
946                .enumerate()
947            {
948                if byte_pos < c_start + c.len() {
949                    pos = Some(cidx);
950                    break;
951                }
952            }
953
954            if let Some(pos) = pos {
955                Ok(TextPosition::new(pos as upos_type, 0))
956            } else {
957                Err(TextError::ByteIndexOutOfBounds(byte_pos, self.text.len()))
958            }
959        }
960
961        /// Byte range to grapheme range.
962        ///
963        /// * byte must <= byte-len.
964        fn bytes_to_range(&self, bytes: Range<usize>) -> Result<TextRange, TextError> {
965            let mut start = None;
966            let mut end = None;
967            for (cidx, (c_start, c)) in self
968                .text
969                .grapheme_indices(true)
970                .chain(once((self.text.len(), " ")))
971                .enumerate()
972            {
973                if bytes.start < c_start + c.len() {
974                    if start.is_none() {
975                        start = Some(cidx as upos_type);
976                    }
977                }
978                if bytes.end < c_start + c.len() {
979                    if end.is_none() {
980                        end = Some(cidx as upos_type);
981                    }
982                }
983                if start.is_some() && end.is_some() {
984                    break;
985                }
986            }
987
988            let Some(start) = start else {
989                return Err(TextError::ByteIndexOutOfBounds(
990                    bytes.start,
991                    self.text.len(),
992                ));
993            };
994            let Some(end) = end else {
995                return Err(TextError::ByteIndexOutOfBounds(bytes.end, self.text.len()));
996            };
997
998            Ok(TextRange::new((start, 0), (end, 0)))
999        }
1000
1001        /// A range of the text as `Cow<str>`.
1002        ///
1003        /// * range must be a valid range. row <= len_lines, col <= line_width of the row.
1004        /// * pos must be inside of range.
1005        fn str_slice(&self, range: TextRange) -> Result<Cow<'_, str>, TextError> {
1006            let range = self.byte_range(range)?;
1007            Ok(Cow::Borrowed(&self.text[range.start..range.end]))
1008        }
1009
1010        /// A range of the text as `Cow<str>`.
1011        ///
1012        /// * range must be valid
1013        fn str_slice_byte(&self, range: Range<usize>) -> Result<Cow<'_, str>, TextError> {
1014            Ok(Cow::Borrowed(&self.text[range.start..range.end]))
1015        }
1016
1017        /// Return a cursor over the graphemes of the range, start at the given position.
1018        ///
1019        /// * range must be a valid range. row <= len_lines, col <= line_width of the row.
1020        /// * pos must be inside of range.
1021        fn graphemes(
1022            &self,
1023            range: TextRange,
1024            pos: TextPosition,
1025        ) -> Result<impl Cursor<Item = Grapheme<'_>>, TextError> {
1026            let range_byte = self.byte_range(range)?;
1027            let pos_byte = self.byte_range_at(pos)?;
1028            Ok(StrGraphemes::new_offset(
1029                range_byte.start,
1030                &self.text[range_byte.clone()],
1031                pos_byte.start - range_byte.start,
1032            ))
1033        }
1034
1035        /// Line as str.
1036        ///
1037        /// * row must be <= len_lines
1038        fn line_at(&self, row: upos_type) -> Result<Cow<'_, str>, TextError> {
1039            if row == 0 {
1040                Ok(Cow::Borrowed(&self.text))
1041            } else if row == 1 {
1042                Ok(Cow::Borrowed(""))
1043            } else {
1044                Err(TextError::LineIndexOutOfBounds(row, 1))
1045            }
1046        }
1047
1048        /// Iterate over text-lines, starting at line-offset.
1049        ///
1050        /// * row must be <= len_lines
1051        fn lines_at(
1052            &self,
1053            row: upos_type,
1054        ) -> Result<impl Iterator<Item = Cow<'_, str>>, TextError> {
1055            if row == 0 {
1056                Ok(once(Cow::Borrowed(self.text.as_str())))
1057            } else if row == 1 {
1058                Ok(once(Cow::Borrowed("")))
1059            } else {
1060                Err(TextError::LineIndexOutOfBounds(row, 1))
1061            }
1062        }
1063
1064        /// Return a line as an iterator over the graphemes.
1065        /// This contains the '\n' at the end.
1066        ///
1067        /// * row must be <= len_lines
1068        fn line_graphemes(
1069            &self,
1070            row: upos_type,
1071        ) -> Result<impl Cursor<Item = Grapheme<'_>>, TextError> {
1072            if row == 0 {
1073                Ok(StrGraphemes::new(0, &self.text))
1074            } else if row == 1 {
1075                Ok(StrGraphemes::new(self.text.len(), ""))
1076            } else {
1077                Err(TextError::LineIndexOutOfBounds(row, 1))
1078            }
1079        }
1080
1081        /// Line width of row as grapheme count.
1082        /// Excludes the terminating '\n'.
1083        ///
1084        /// * row must be <= len_lines
1085        fn line_width(&self, row: upos_type) -> Result<upos_type, TextError> {
1086            if row == 0 {
1087                Ok(self.len)
1088            } else if row == 1 {
1089                Ok(0)
1090            } else {
1091                Err(TextError::LineIndexOutOfBounds(row, 1))
1092            }
1093        }
1094
1095        /// Number of lines.
1096        fn len_lines(&self) -> upos_type {
1097            1
1098        }
1099
1100        /// Insert a char at the given position.
1101        ///
1102        /// * range must be a valid range. row <= len_lines, col <= line_width of the row.
1103        fn insert_char(
1104            &mut self,
1105            pos: TextPosition,
1106            c: char,
1107        ) -> Result<(TextRange, Range<usize>), TextError> {
1108            if pos.y != 0 && pos != TextPosition::new(0, 1) {
1109                return Err(TextError::TextPositionOutOfBounds(pos));
1110            }
1111
1112            let byte_pos = self.byte_range_at(pos)?;
1113            let (before, after) = self.text.split_at(byte_pos.start);
1114
1115            let old_len = self.len;
1116            self.buf.clear();
1117            self.buf.push_str(before);
1118            self.buf.push(c);
1119            self.buf.push_str(after);
1120
1121            let before_bytes = before.len();
1122            let new_len = str_len(&self.buf);
1123
1124            mem::swap(&mut self.text, &mut self.buf);
1125            self.len = new_len;
1126
1127            Ok((
1128                TextRange::new((pos.x, 0), (pos.x + (new_len - old_len), 0)),
1129                before_bytes..before_bytes + c.len_utf8(),
1130            ))
1131        }
1132
1133        /// Insert a str at position.
1134        fn insert_str(
1135            &mut self,
1136            pos: TextPosition,
1137            t: &str,
1138        ) -> Result<(TextRange, Range<usize>), TextError> {
1139            if pos.y != 0 && pos != TextPosition::new(0, 1) {
1140                return Err(TextError::TextPositionOutOfBounds(pos));
1141            }
1142
1143            let byte_pos = self.byte_range_at(pos)?;
1144            let (before, after) = self.text.split_at(byte_pos.start);
1145
1146            let old_len = self.len;
1147            self.buf.clear();
1148            self.buf.push_str(before);
1149            self.buf.push_str(t);
1150            self.buf.push_str(after);
1151
1152            let before_bytes = before.len();
1153            let new_len = str_len(&self.buf);
1154
1155            mem::swap(&mut self.text, &mut self.buf);
1156            self.len = new_len;
1157
1158            Ok((
1159                TextRange::new((pos.x, 0), (pos.x + (new_len - old_len), 0)),
1160                before_bytes..before_bytes + t.len(),
1161            ))
1162        }
1163
1164        /// Remove a range.
1165        fn remove(
1166            &mut self,
1167            range: TextRange,
1168        ) -> Result<(String, (TextRange, Range<usize>)), TextError> {
1169            if range.start.y != 0 && range.start != TextPosition::new(0, 1) {
1170                return Err(TextError::TextRangeOutOfBounds(range));
1171            }
1172            if range.end.y != 0 && range.end != TextPosition::new(0, 1) {
1173                return Err(TextError::TextRangeOutOfBounds(range));
1174            }
1175
1176            let bytes = self.byte_range(range)?;
1177
1178            let (before, remove, after) = (
1179                &self.text[..bytes.start],
1180                &self.text[bytes.start..bytes.end],
1181                &self.text[bytes.end..],
1182            );
1183
1184            self.buf.clear();
1185            self.buf.push_str(before);
1186            self.buf.push_str(after);
1187
1188            let remove_str = remove.to_string();
1189            let before_bytes = before.len();
1190            let remove_bytes = remove.len();
1191            let new_len = str_len(&self.buf);
1192
1193            mem::swap(&mut self.text, &mut self.buf);
1194            self.len = new_len;
1195
1196            Ok((
1197                remove_str,
1198                (range, before_bytes..before_bytes + remove_bytes),
1199            ))
1200        }
1201
1202        /// Insert a string at the given byte index.
1203        fn insert_b(&mut self, byte_pos: usize, t: &str) -> Result<(), TextError> {
1204            let Some((before, after)) = self.text.split_at_checked(byte_pos) else {
1205                return Err(TextError::ByteIndexNotCharBoundary(byte_pos));
1206            };
1207
1208            self.buf.clear();
1209            self.buf.push_str(before);
1210            self.buf.push_str(t);
1211            self.buf.push_str(after);
1212            let new_len = str_len(&self.buf);
1213
1214            mem::swap(&mut self.text, &mut self.buf);
1215            self.len = new_len;
1216
1217            Ok(())
1218        }
1219
1220        /// Remove the given byte-range.
1221        fn remove_b(&mut self, byte_range: Range<usize>) -> Result<(), TextError> {
1222            let Some((before, after)) = self.text.split_at_checked(byte_range.start) else {
1223                return Err(TextError::ByteIndexNotCharBoundary(byte_range.start));
1224            };
1225            let Some((_remove, after)) = after.split_at_checked(byte_range.end - byte_range.start)
1226            else {
1227                return Err(TextError::ByteIndexNotCharBoundary(byte_range.end));
1228            };
1229
1230            self.buf.clear();
1231            self.buf.push_str(before);
1232            self.buf.push_str(after);
1233            let new_len = str_len(&self.buf);
1234
1235            mem::swap(&mut self.text, &mut self.buf);
1236            self.len = new_len;
1237
1238            Ok(())
1239        }
1240    }
1241}