1use crate::cache::{Cache, LineWidthCache};
2use crate::clipboard::Clipboard;
3use crate::glyph2::{GlyphIter2, TextWrap2};
4use crate::grapheme::Grapheme;
5use crate::range_map::{RangeMap, expand_range_by, ranges_intersect, shrink_range_by};
6use crate::text_store::TextStore;
7use crate::undo_buffer::{StyleChange, TextPositionChange, UndoBuffer, UndoEntry, UndoOp};
8use crate::{TextError, TextPosition, TextRange, upos_type};
9use dyn_clone::clone_box;
10use ratatui::layout::Size;
11use std::borrow::Cow;
12use std::cell::Cell;
13use std::cmp::min;
14use std::ops::Range;
15use std::rc::Rc;
16
17pub mod core_op;
18
19#[derive(Debug, Default, PartialEq, Eq, Clone, Copy, Hash)]
20pub(crate) struct TextCursor {
21 pub(crate) anchor: TextPosition,
22 pub(crate) cursor: TextPosition,
23}
24
25impl TextCursor {
26 pub fn new(anchor: TextPosition, cursor: TextPosition) -> Self {
27 Self { anchor, cursor }
28 }
29}
30
31#[derive(Debug)]
33pub struct TextCore<Store> {
34 text: Store,
36
37 cursor: Rc<Cell<TextCursor>>,
38
39 styles: Option<Box<RangeMap>>,
41 undo: Option<Box<dyn UndoBuffer>>,
43 clip: Option<Box<dyn Clipboard>>,
45 cache: Cache,
47
48 glyph_ctrl: bool,
50 wrap_ctrl: bool,
52}
53
54impl<Store: Clone> Clone for TextCore<Store> {
55 fn clone(&self) -> Self {
56 Self {
57 text: self.text.clone(),
58 cursor: self.cursor.clone(),
59 styles: self.styles.clone(),
60 undo: self.undo.as_ref().map(|v| clone_box(v.as_ref())),
61 clip: self.clip.as_ref().map(|v| clone_box(v.as_ref())),
62 cache: Default::default(),
63 glyph_ctrl: self.glyph_ctrl,
64 wrap_ctrl: self.wrap_ctrl,
65 }
66 }
67}
68
69impl<Store: TextStore + Default> TextCore<Store> {
70 pub fn new(undo: Option<Box<dyn UndoBuffer>>, clip: Option<Box<dyn Clipboard>>) -> Self {
71 Self {
72 text: Store::default(),
73 cursor: Default::default(),
74 styles: Default::default(),
75 undo,
76 clip,
77 cache: Default::default(),
78 glyph_ctrl: false,
79 wrap_ctrl: false,
80 }
81 }
82
83 #[inline]
85 pub fn set_glyph_ctrl(&mut self, show_ctrl: bool) {
86 self.glyph_ctrl = show_ctrl;
87 }
88
89 pub fn glyph_ctrl(&self) -> bool {
91 self.glyph_ctrl
92 }
93
94 #[inline]
96 pub fn set_wrap_ctrl(&mut self, wrap_ctrl: bool) {
97 self.wrap_ctrl = wrap_ctrl;
98 }
99
100 pub fn wrap_ctrl(&self) -> bool {
102 self.wrap_ctrl
103 }
104}
105
106impl<Store: TextStore + Default> TextCore<Store> {
107 pub fn set_clipboard(&mut self, clip: Option<Box<dyn Clipboard + 'static>>) {
109 self.clip = clip;
110 }
111
112 pub fn clipboard(&self) -> Option<&dyn Clipboard> {
114 match &self.clip {
115 None => None,
116 Some(v) => Some(v.as_ref()),
117 }
118 }
119}
120
121impl<Store: TextStore + Default> TextCore<Store> {
122 #[inline]
124 pub fn set_undo_buffer(&mut self, undo: Option<Box<dyn UndoBuffer>>) {
125 self.undo = undo;
126 }
127
128 #[inline]
130 pub fn set_undo_count(&mut self, n: u32) {
131 if let Some(undo) = self.undo.as_mut() {
132 undo.set_undo_count(n);
133 };
134 }
135
136 #[inline]
138 pub fn begin_undo_seq(&mut self) {
139 if let Some(undo) = self.undo.as_mut() {
140 undo.begin_seq();
141 };
142 }
143
144 #[inline]
146 pub fn end_undo_seq(&mut self) {
147 if let Some(undo) = self.undo.as_mut() {
148 undo.end_seq();
149 };
150 }
151
152 #[inline]
154 pub fn undo_buffer(&self) -> Option<&dyn UndoBuffer> {
155 match &self.undo {
156 None => None,
157 Some(v) => Some(v.as_ref()),
158 }
159 }
160
161 #[inline]
163 pub fn undo_buffer_mut(&mut self) -> Option<&mut dyn UndoBuffer> {
164 match &mut self.undo {
165 None => None,
166 Some(v) => Some(v.as_mut()),
167 }
168 }
169
170 pub fn undo(&mut self) -> bool {
172 let Some(undo) = self.undo.as_mut() else {
173 return false;
174 };
175
176 undo.append(UndoOp::Undo);
177
178 self._undo()
179 }
180
181 fn _undo(&mut self) -> bool {
183 let Some(undo) = self.undo.as_mut() else {
184 return false;
185 };
186 let undo_op = undo.undo();
187 let changed = !undo_op.is_empty();
188 for op in undo_op {
189 match op {
190 UndoOp::InsertChar {
191 bytes,
192 cursor,
193 anchor,
194 ..
195 }
196 | UndoOp::InsertStr {
197 bytes,
198 cursor,
199 anchor,
200 ..
201 } => {
202 self.text.remove_b(bytes.clone()).expect("valid_bytes");
203
204 if let Some(sty) = &mut self.styles {
205 sty.remap(|r, _| Some(shrink_range_by(bytes.clone(), r)));
206 }
207 self.cursor
208 .set(TextCursor::new(anchor.before, cursor.before));
209 }
210 UndoOp::RemoveStr {
211 bytes,
212 cursor,
213 anchor,
214 txt,
215 styles,
216 }
217 | UndoOp::RemoveChar {
218 bytes,
219 cursor,
220 anchor,
221 txt,
222 styles,
223 } => {
224 self.text.insert_b(bytes.start, txt).expect("valid_bytes");
225
226 if let Some(sty) = &mut self.styles {
227 for s in styles {
228 sty.remove(s.after.clone(), s.style);
229 }
230 for s in styles {
231 sty.add(s.before.clone(), s.style);
232 }
233 sty.remap(|r, _| {
234 if ranges_intersect(bytes.clone(), r.clone()) {
235 Some(r)
236 } else {
237 Some(expand_range_by(bytes.clone(), r))
238 }
239 });
240 }
241 self.cursor
242 .set(TextCursor::new(anchor.before, cursor.before));
243 }
244 UndoOp::Cursor { cursor, anchor } => {
245 self.cursor
246 .set(TextCursor::new(anchor.before, cursor.before));
247 }
248 UndoOp::SetStyles { styles_before, .. } => {
249 if let Some(sty) = &mut self.styles {
250 sty.set(styles_before.iter().cloned());
251 }
252 }
253 UndoOp::SetText { .. } | UndoOp::Undo | UndoOp::Redo => {
254 unreachable!()
255 }
256 }
257 }
258 changed
259 }
260
261 pub fn redo(&mut self) -> bool {
263 let Some(undo) = self.undo.as_mut() else {
264 return false;
265 };
266
267 undo.append(UndoOp::Redo);
268
269 self._redo()
270 }
271
272 fn _redo(&mut self) -> bool {
273 let Some(undo) = self.undo.as_mut() else {
274 return false;
275 };
276 let redo_op = undo.redo();
277 let changed = !redo_op.is_empty();
278 for op in redo_op {
279 match op {
280 UndoOp::InsertChar {
281 bytes,
282 cursor,
283 anchor,
284 txt,
285 }
286 | UndoOp::InsertStr {
287 bytes,
288 cursor,
289 anchor,
290 txt,
291 } => {
292 self.text.insert_b(bytes.start, txt).expect("valid_bytes");
293 if let Some(sty) = &mut self.styles {
294 sty.remap(|r, _| Some(expand_range_by(bytes.clone(), r)));
295 }
296 self.cursor.set(TextCursor::new(anchor.after, cursor.after));
297 }
298 UndoOp::RemoveChar {
299 bytes,
300 cursor,
301 anchor,
302 styles,
303 ..
304 }
305 | UndoOp::RemoveStr {
306 bytes,
307 cursor,
308 anchor,
309 styles,
310 ..
311 } => {
312 self.text.remove_b(bytes.clone()).expect("valid_bytes");
313
314 if let Some(sty) = &mut self.styles {
315 sty.remap(|r, _| {
316 if ranges_intersect(bytes.clone(), r.clone()) {
317 Some(r)
318 } else {
319 Some(shrink_range_by(bytes.clone(), r))
320 }
321 });
322 for s in styles {
323 sty.remove(s.before.clone(), s.style);
324 }
325 for s in styles {
326 sty.add(s.after.clone(), s.style);
327 }
328 }
329 self.cursor.set(TextCursor::new(anchor.after, cursor.after));
330 }
331 UndoOp::Cursor { cursor, anchor } => {
332 self.cursor.set(TextCursor::new(anchor.after, cursor.after));
333 }
334
335 UndoOp::SetStyles { styles_after, .. } => {
336 if let Some(sty) = &mut self.styles {
337 sty.set(styles_after.iter().cloned());
338 }
339 }
340 UndoOp::SetText { .. } | UndoOp::Undo | UndoOp::Redo => {
341 unreachable!()
342 }
343 }
344 }
345 changed
346 }
347
348 pub fn recent_replay_log(&mut self) -> Vec<UndoEntry> {
350 if let Some(undo) = &mut self.undo {
351 undo.recent_replay_log()
352 } else {
353 Vec::default()
354 }
355 }
356
357 pub fn replay_log(&mut self, replay: &[UndoEntry]) {
359 for replay_entry in replay {
360 match &replay_entry.operation {
361 UndoOp::SetText { txt } => {
362 self.text.set_string(txt);
363 if let Some(sty) = &mut self.styles {
364 sty.clear();
365 }
366 if let Some(undo) = self.undo.as_mut() {
367 undo.clear();
368 };
369 }
370 UndoOp::InsertChar { bytes, txt, .. } | UndoOp::InsertStr { bytes, txt, .. } => {
371 self.text.insert_b(bytes.start, txt).expect("valid_range");
372 if let Some(sty) = &mut self.styles {
373 sty.remap(|r, _| Some(expand_range_by(bytes.clone(), r)));
374 }
375 }
376 UndoOp::RemoveChar { bytes, styles, .. }
377 | UndoOp::RemoveStr { bytes, styles, .. } => {
378 self.text.remove_b(bytes.clone()).expect("valid_range");
379 if let Some(sty) = &mut self.styles {
380 sty.remap(|r, _| {
381 if ranges_intersect(bytes.clone(), r.clone()) {
382 Some(r)
383 } else {
384 Some(shrink_range_by(bytes.clone(), r))
385 }
386 });
387 for s in styles {
388 sty.remove(s.before.clone(), s.style);
389 }
390 for s in styles {
391 sty.add(s.after.clone(), s.style);
392 }
393 }
394 }
395 UndoOp::Cursor { .. } => {
396 }
398
399 UndoOp::SetStyles { styles_after, .. } => {
400 self.init_styles();
401 if let Some(sty) = &mut self.styles {
402 sty.set(styles_after.iter().cloned());
403 }
404 }
405 UndoOp::Undo => {
406 self._undo();
407 }
408 UndoOp::Redo => {
409 self._redo();
410 }
411 }
412
413 if let Some(undo) = self.undo.as_mut() {
414 undo.append_from_replay(replay_entry.clone());
415 };
416 }
417 }
418}
419
420impl<Store: TextStore + Default> TextCore<Store> {
421 fn init_styles(&mut self) {
422 if self.styles.is_none() {
423 self.styles = Some(Box::new(RangeMap::default()));
424 }
425 }
426
427 #[inline]
432 pub fn set_styles(&mut self, new_styles: Vec<(Range<usize>, usize)>) {
433 self.init_styles();
434
435 let Some(sty) = &mut self.styles else {
436 return;
437 };
438 if let Some(undo) = &mut self.undo {
439 if undo.undo_styles_enabled() || undo.has_replay_log() {
440 undo.append(UndoOp::SetStyles {
441 styles_before: sty.values().collect::<Vec<_>>(),
442 styles_after: new_styles.clone(),
443 });
444 }
445 }
446 sty.set(new_styles.into_iter());
447 }
448
449 #[inline]
454 pub fn add_style(&mut self, range: Range<usize>, style: usize) {
455 self.init_styles();
456
457 if let Some(sty) = &mut self.styles {
458 sty.add(range.clone(), style);
459 }
460 }
461
462 #[inline]
466 pub fn remove_style(&mut self, range: Range<usize>, style: usize) {
467 if let Some(sty) = &mut self.styles {
468 sty.remove(range.clone(), style);
469 }
470 }
471
472 #[inline]
474 pub fn remove_style_fully(&mut self, style: usize) {
475 let Some(sty) = self.styles.as_mut() else {
476 return;
477 };
478 let styles = sty
479 .values()
480 .filter(|(_, s)| *s == style)
481 .collect::<Vec<_>>();
482 for (range, style) in &styles {
483 sty.remove(range.clone(), *style);
484 }
485 }
486
487 #[inline]
492 pub(crate) fn styles_at_page(&self, pos: usize, range: Range<usize>, buf: &mut Vec<usize>) {
493 if let Some(sty) = &self.styles {
494 sty.values_at_page(pos, range, buf);
495 }
496 }
497
498 #[inline]
500 pub fn styles_in(&self, range: Range<usize>, buf: &mut Vec<(Range<usize>, usize)>) {
501 if let Some(sty) = &self.styles {
502 sty.values_in(range, buf);
503 }
504 }
505
506 #[inline]
508 pub fn styles_in_match(
509 &self,
510 range: Range<usize>,
511 style: usize,
512 buf: &mut Vec<(Range<usize>, usize)>,
513 ) {
514 if let Some(sty) = &self.styles {
515 sty.values_in_match(range, style, buf);
516 }
517 }
518
519 #[inline]
521 pub fn styles_at(&self, byte_pos: usize, buf: &mut Vec<(Range<usize>, usize)>) {
522 if let Some(sty) = &self.styles {
523 sty.values_at(byte_pos, buf);
524 }
525 }
526
527 #[inline]
529 pub fn styles_at_match(&self, byte_pos: usize, style: usize) -> Option<Range<usize>> {
530 if let Some(sty) = &self.styles {
531 sty.value_match(byte_pos, style)
532 } else {
533 None
534 }
535 }
536
537 #[inline]
539 pub fn styles(&self) -> Option<impl Iterator<Item = (Range<usize>, usize)> + '_> {
540 self.styles.as_ref().map(|v| v.values())
541 }
542}
543
544impl<Store: TextStore + Default> TextCore<Store> {
545 pub(crate) fn shared_cursor(&self) -> Rc<Cell<TextCursor>> {
547 self.cursor.clone()
548 }
549
550 pub fn set_cursor(&mut self, cursor: TextPosition, extend_selection: bool) -> bool {
556 let old_cursor = self.cursor.get();
557
558 let cursor_pos = TextPosition::new(
559 min(cursor.x, self.line_width(cursor.y).expect("valid-line")),
560 min(cursor.y, self.len_lines()),
561 );
562 let new_cursor = TextCursor {
563 cursor: cursor_pos,
564 anchor: if !extend_selection {
565 cursor_pos
566 } else {
567 old_cursor.anchor
568 },
569 };
570 self.cursor.set(new_cursor);
571
572 if let Some(undo) = self.undo.as_mut() {
573 undo.append(UndoOp::Cursor {
574 cursor: TextPositionChange {
575 before: old_cursor.cursor,
576 after: new_cursor.cursor,
577 },
578 anchor: TextPositionChange {
579 before: old_cursor.anchor,
580 after: new_cursor.anchor,
581 },
582 });
583 }
584
585 old_cursor != self.cursor.get()
586 }
587
588 #[inline]
590 pub fn cursor(&self) -> TextPosition {
591 self.cursor.get().cursor
592 }
593
594 #[inline]
596 pub fn anchor(&self) -> TextPosition {
597 self.cursor.get().anchor
598 }
599
600 #[inline]
602 pub fn has_selection(&self) -> bool {
603 let cursor = self.cursor.get();
604 cursor.anchor != cursor.cursor
605 }
606
607 #[inline]
610 pub fn set_selection(&mut self, anchor: TextPosition, cursor: TextPosition) -> bool {
611 let old_selection = self.selection();
612
613 self.set_cursor(anchor, false);
614 self.set_cursor(cursor, true);
615
616 old_selection != self.selection()
617 }
618
619 #[inline]
621 pub fn select_all(&mut self) -> bool {
622 let old_selection = self.selection();
623
624 self.set_cursor(TextPosition::new(0, self.len_lines()), false);
625 self.set_cursor(TextPosition::new(0, 0), true);
626
627 old_selection != self.selection()
628 }
629
630 #[inline]
632 pub fn selection(&self) -> TextRange {
633 let cursor = self.cursor.get();
634 #[allow(clippy::comparison_chain)]
635 if cursor.cursor.y < cursor.anchor.y {
636 TextRange {
637 start: cursor.cursor,
638 end: cursor.anchor,
639 }
640 } else if cursor.cursor.y > cursor.anchor.y {
641 TextRange {
642 start: cursor.anchor,
643 end: cursor.cursor,
644 }
645 } else {
646 if cursor.cursor.x < cursor.anchor.x {
647 TextRange {
648 start: cursor.cursor,
649 end: cursor.anchor,
650 }
651 } else {
652 TextRange {
653 start: cursor.anchor,
654 end: cursor.cursor,
655 }
656 }
657 }
658 }
659}
660
661impl<Store: TextStore + Default> TextCore<Store> {
662 pub(crate) fn cache_validity(&self) -> Option<usize> {
667 self.text.cache_validity()
668 }
669}
670
671impl<Store: TextStore + Default> TextCore<Store> {
672 #[inline]
674 pub fn is_empty(&self) -> bool {
675 self.len_bytes() == 0
676 }
677
678 #[inline]
681 pub fn byte_at(&self, pos: TextPosition) -> Result<Range<usize>, TextError> {
682 self.text.byte_range_at(pos)
683 }
684
685 #[inline]
687 pub fn bytes_at_range(&self, range: TextRange) -> Result<Range<usize>, TextError> {
688 self.text.byte_range(range)
689 }
690
691 #[inline]
694 pub fn byte_pos(&self, byte: usize) -> Result<TextPosition, TextError> {
695 self.text.byte_to_pos(byte)
696 }
697
698 #[inline]
700 pub fn byte_range(&self, bytes: Range<usize>) -> Result<TextRange, TextError> {
701 self.text.bytes_to_range(bytes)
702 }
703
704 #[inline]
706 pub fn str_slice(&self, range: TextRange) -> Result<Cow<'_, str>, TextError> {
707 self.text.str_slice(range)
708 }
709
710 #[inline]
712 pub fn str_slice_byte(&self, range: Range<usize>) -> Result<Cow<'_, str>, TextError> {
713 self.text.str_slice_byte(range)
714 }
715
716 #[inline]
719 pub fn cache(&self) -> &Cache {
720 &self.cache
721 }
722
723 #[allow(clippy::too_many_arguments)]
725 pub(crate) fn fill_cache(
726 &self,
727 rendered: Size,
728 sub_row_offset: upos_type,
729 rows: Range<upos_type>,
730 tab_width: u32,
731 text_wrap: TextWrap2,
732 ctrl_char: bool,
733 left_margin: upos_type,
734 right_margin: upos_type,
735 word_margin: upos_type,
736 ) -> Result<(), TextError> {
737 _ = self.glyphs2(
738 rendered,
739 sub_row_offset,
740 rows,
741 tab_width,
742 text_wrap,
743 ctrl_char,
744 left_margin,
745 right_margin,
746 word_margin,
747 )?;
748 Ok(())
749 }
750
751 #[inline]
754 #[allow(clippy::too_many_arguments)]
755 pub(crate) fn glyphs2(
756 &self,
757 rendered: Size,
758 sub_row_offset: upos_type,
759 rows: Range<upos_type>,
760 tab_width: u32,
761 text_wrap: TextWrap2,
762 ctrl_char: bool,
763 left_margin: upos_type,
764 right_margin: upos_type,
765 word_margin: upos_type,
766 ) -> Result<GlyphIter2<'_, Store::GraphemeIter<'_>>, TextError> {
767 self.cache.validate(
768 text_wrap,
769 left_margin,
770 rendered.width as upos_type,
771 rendered.height as upos_type,
772 ctrl_char,
773 self.cache_validity(),
774 );
775
776 let range = TextRange::new((sub_row_offset, rows.start), (0, rows.end));
777
778 let range_bytes;
779 let mut range_to_bytes = self.cache.range_to_bytes.borrow_mut();
780 if let Some(cache) = range_to_bytes.get(&range) {
781 range_bytes = cache.clone();
782 } else {
783 let cache = self.text.byte_range(range)?;
784 range_to_bytes.insert(range, cache.clone());
785 range_bytes = cache;
786 }
787
788 let iter = self.graphemes_byte(range_bytes.clone(), range_bytes.start)?;
789
790 let mut it = GlyphIter2::new(
791 range.start, range_bytes.start,
793 iter,
794 self.cache.clone(),
795 );
796 it.set_tabs(tab_width);
797 it.set_show_ctrl(self.glyph_ctrl);
798 it.set_wrap_ctrl(self.wrap_ctrl);
799 it.set_lf_breaks(self.text().is_multi_line());
800 it.set_text_wrap(text_wrap);
801 it.set_left_margin(left_margin);
802 it.set_right_margin(right_margin);
803 it.set_word_margin(word_margin);
804 it.prepare()?;
805 Ok(it)
806 }
807
808 #[inline]
810 pub fn grapheme_at(&self, pos: TextPosition) -> Result<Option<Grapheme<'_>>, TextError> {
811 let range_bytes = self.bytes_at_range(TextRange::new(pos, (pos.x + 1, pos.y)))?;
812 let pos_byte = self.byte_at(pos)?.start;
813
814 let mut it = self.text.graphemes_byte(range_bytes, pos_byte)?;
815
816 Ok(it.next())
817 }
818
819 #[inline]
821 pub fn text_graphemes(&self, pos: TextPosition) -> Result<Store::GraphemeIter<'_>, TextError> {
822 let rows = self.len_lines() - 1;
823 let cols = self.line_width(rows).expect("valid_row");
824
825 let range_bytes = self.bytes_at_range(TextRange::new((0, 0), (cols, rows)))?;
826 let pos_byte = self.byte_at(pos)?.start;
827
828 self.text.graphemes_byte(range_bytes, pos_byte)
829 }
830
831 #[inline]
833 pub fn graphemes(
834 &self,
835 range: TextRange,
836 pos: TextPosition,
837 ) -> Result<Store::GraphemeIter<'_>, TextError> {
838 let range_bytes = self.bytes_at_range(range)?;
839 let pos_byte = self.byte_at(pos)?.start;
840
841 self.text.graphemes_byte(range_bytes, pos_byte)
842 }
843
844 #[inline]
846 pub fn graphemes_byte(
847 &self,
848 range: Range<usize>,
849 pos: usize,
850 ) -> Result<Store::GraphemeIter<'_>, TextError> {
851 self.text.graphemes_byte(range, pos)
852 }
853
854 #[inline]
858 pub fn line_at(&self, row: upos_type) -> Result<Cow<'_, str>, TextError> {
859 self.text.line_at(row)
860 }
861
862 #[inline]
866 pub fn lines_at(
867 &self,
868 row: upos_type,
869 ) -> Result<impl Iterator<Item = Cow<'_, str>>, TextError> {
870 self.text.lines_at(row)
871 }
872
873 #[inline]
875 pub fn line_graphemes(&self, row: upos_type) -> Result<Store::GraphemeIter<'_>, TextError> {
876 self.text.line_graphemes(row)
877 }
878
879 #[inline]
881 pub fn line_width(&self, row: upos_type) -> Result<upos_type, TextError> {
882 self.cache.validate_byte_pos(self.cache_validity());
883
884 let mut line_width = self.cache.line_width.borrow_mut();
885 if let Some(cache) = line_width.get(&row) {
886 Ok(cache.width)
887 } else {
888 let width = self.text.line_width(row)?;
889 let byte_pos = self.text.byte_range_at(TextPosition::new(width, row))?;
890 line_width.insert(
891 row,
892 LineWidthCache {
893 width,
894 byte_pos: byte_pos.start,
895 },
896 );
897 Ok(width)
898 }
899 }
900
901 #[inline]
903 pub fn len_lines(&self) -> upos_type {
904 self.text.len_lines()
905 }
906
907 #[inline]
909 pub fn len_bytes(&self) -> usize {
910 self.text.len_bytes()
911 }
912}
913
914impl<Store: TextStore + Default> TextCore<Store> {
915 pub fn clear(&mut self) {
917 self.text.set_string(Default::default());
918 self.cursor.set(Default::default());
919 if let Some(sty) = &mut self.styles {
920 sty.clear();
921 }
922 if let Some(undo) = &mut self.undo {
923 undo.clear();
924
925 if undo.has_replay_log() {
926 undo.append(UndoOp::SetText {
927 txt: self.text.string(),
928 });
929 }
930 }
931 }
932
933 pub fn text(&self) -> &Store {
935 &self.text
936 }
937
938 pub fn set_text(&mut self, t: Store) -> bool {
941 self.text = t;
942 if let Some(sty) = &mut self.styles {
943 sty.clear();
944 }
945 self.cache.clear();
946 self.cursor.set(Default::default());
947
948 if let Some(undo) = &mut self.undo {
949 undo.clear();
950
951 if undo.has_replay_log() {
952 undo.append(UndoOp::SetText {
953 txt: self.text.string(),
954 });
955 }
956 }
957
958 true
959 }
960
961 pub fn insert_char(&mut self, pos: TextPosition, c: char) -> Result<bool, TextError> {
967 let (inserted_range, inserted_bytes) = self.text.insert_char(pos, c)?;
968
969 let old_cursor = self.cursor.get();
970
971 if let Some(sty) = &mut self.styles {
972 sty.remap(|r, _| Some(expand_range_by(inserted_bytes.clone(), r)));
973 }
974
975 let new_cursor = TextCursor {
976 anchor: inserted_range.expand_pos(old_cursor.cursor),
977 cursor: inserted_range.expand_pos(old_cursor.anchor),
978 };
979 self.cursor.set(new_cursor);
980
981 if let Some(undo) = self.undo.as_mut() {
982 undo.append(UndoOp::InsertChar {
983 bytes: inserted_bytes.clone(),
984 cursor: TextPositionChange {
985 before: old_cursor.cursor,
986 after: new_cursor.cursor,
987 },
988 anchor: TextPositionChange {
989 before: old_cursor.anchor,
990 after: new_cursor.anchor,
991 },
992 txt: c.to_string(),
993 });
994 }
995
996 Ok(true)
997 }
998
999 pub fn insert_str(&mut self, pos: TextPosition, t: &str) -> Result<bool, TextError> {
1001 let old_cursor = self.cursor.get();
1002
1003 let (inserted_range, inserted_bytes) = self.text.insert_str(pos, t)?;
1004
1005 if let Some(sty) = &mut self.styles {
1006 sty.remap(|r, _| Some(expand_range_by(inserted_bytes.clone(), r)));
1007 }
1008 let new_cursor = TextCursor {
1009 anchor: inserted_range.expand_pos(old_cursor.anchor),
1010 cursor: inserted_range.expand_pos(old_cursor.cursor),
1011 };
1012 self.cursor.set(new_cursor);
1013
1014 if let Some(undo) = self.undo.as_mut() {
1015 undo.append(UndoOp::InsertStr {
1016 bytes: inserted_bytes.clone(),
1017 cursor: TextPositionChange {
1018 before: old_cursor.cursor,
1019 after: new_cursor.cursor,
1020 },
1021 anchor: TextPositionChange {
1022 before: old_cursor.anchor,
1023 after: new_cursor.anchor,
1024 },
1025 txt: t.to_string(),
1026 });
1027 }
1028
1029 Ok(true)
1030 }
1031
1032 pub fn remove_char_range(&mut self, range: TextRange) -> Result<bool, TextError> {
1037 self._remove_range(range, true)
1038 }
1039
1040 pub fn remove_str_range(&mut self, range: TextRange) -> Result<bool, TextError> {
1044 self._remove_range(range, false)
1045 }
1046
1047 fn _remove_range(&mut self, range: TextRange, char_range: bool) -> Result<bool, TextError> {
1048 let old_cursor = self.cursor.get();
1049
1050 if range.is_empty() {
1051 return Ok(false);
1052 }
1053
1054 let (old_text, (_removed_range, removed_bytes)) = self.text.remove(range)?;
1055
1056 let mut changed_style = Vec::new();
1058 if let Some(sty) = &mut self.styles {
1059 sty.remap(|r, s| {
1060 let new_range = shrink_range_by(removed_bytes.clone(), r.clone());
1061 if ranges_intersect(r.clone(), removed_bytes.clone()) {
1062 changed_style.push(StyleChange {
1063 before: r.clone(),
1064 after: new_range.clone(),
1065 style: s,
1066 });
1067 if new_range.is_empty() {
1068 None
1069 } else {
1070 Some(new_range)
1071 }
1072 } else {
1073 Some(new_range)
1074 }
1075 });
1076 }
1077 let new_cursor = TextCursor {
1078 anchor: range.shrink_pos(old_cursor.anchor),
1079 cursor: range.shrink_pos(old_cursor.cursor),
1080 };
1081 self.cursor.set(new_cursor);
1082
1083 if let Some(undo) = &mut self.undo {
1084 if char_range {
1085 undo.append(UndoOp::RemoveChar {
1086 bytes: removed_bytes.clone(),
1087 cursor: TextPositionChange {
1088 before: old_cursor.cursor,
1089 after: new_cursor.cursor,
1090 },
1091 anchor: TextPositionChange {
1092 before: old_cursor.anchor,
1093 after: new_cursor.anchor,
1094 },
1095 txt: old_text,
1096 styles: changed_style,
1097 });
1098 } else {
1099 undo.append(UndoOp::RemoveStr {
1100 bytes: removed_bytes.clone(),
1101 cursor: TextPositionChange {
1102 before: old_cursor.cursor,
1103 after: new_cursor.cursor,
1104 },
1105 anchor: TextPositionChange {
1106 before: old_cursor.anchor,
1107 after: new_cursor.anchor,
1108 },
1109 txt: old_text,
1110 styles: changed_style,
1111 });
1112 }
1113 }
1114
1115 Ok(true)
1116 }
1117}