1use crate::clipboard::ImageContent;
4use crate::core::event::KeyEvent;
5use crate::text::buffer::TextBufferCore;
6use crate::text::edit::TextEditKind;
7use crate::utils::text::clamp_cursor;
8use crate::widgets::{TextAreaEvent, TextAreaSentinel};
9use std::ops::{Deref, DerefMut};
10
11const SENTINEL_SNAPSHOT_LIMIT: usize = 1000;
12
13#[derive(Clone, Debug)]
22pub struct TextEditor {
23 pub(crate) core: TextBufferCore,
24 pending_external_paste: Option<PendingExternalPaste>,
25 image_snapshots: Vec<ImageSnapshot>,
26 sentinel_snapshots: Vec<SentinelSnapshot>,
27 pub(crate) visual_nav_col: Option<usize>,
33}
34
35#[derive(Clone, Debug)]
36struct PendingExternalPaste {
37 text_before: String,
38 cursor_before: usize,
39 anchor_before: Option<usize>,
40}
41
42#[derive(Clone, Debug)]
43struct ImageSnapshot {
44 text: String,
45 images: Vec<ImageContent>,
46}
47
48#[derive(Clone, Debug)]
49struct SentinelSnapshot {
50 text: String,
51 sentinels: Vec<TextAreaSentinel>,
52}
53
54impl Default for TextEditor {
55 fn default() -> Self {
56 Self::new("")
57 }
58}
59
60impl PartialEq for TextEditor {
61 fn eq(&self, other: &Self) -> bool {
62 self.core == other.core
63 }
64}
65
66impl Eq for TextEditor {}
67
68impl Deref for TextEditor {
69 type Target = TextBufferCore;
70
71 fn deref(&self) -> &Self::Target {
72 &self.core
73 }
74}
75
76impl DerefMut for TextEditor {
77 fn deref_mut(&mut self) -> &mut Self::Target {
78 &mut self.core
79 }
80}
81
82impl TextEditor {
83 pub fn new(text: impl Into<String>) -> Self {
85 let text = text.into();
86 Self {
87 core: TextBufferCore {
88 text,
89 cursor: 0,
90 anchor: None,
91 history: TextBufferCore::fresh_history(),
92 last_edit: None,
93 },
94 pending_external_paste: None,
95 image_snapshots: Vec::new(),
96 sentinel_snapshots: Vec::new(),
97 visual_nav_col: None,
98 }
99 }
100
101 pub fn visual_nav_col(&self) -> Option<usize> {
103 self.visual_nav_col
104 }
105
106 pub fn set_visual_nav_col(&mut self, col: Option<usize>) {
108 self.visual_nav_col = col;
109 }
110
111 pub(crate) fn sync_from(&mut self, text: &str, cursor: usize, anchor: Option<usize>) {
117 let prev_cursor = self.core.cursor;
118 let text_changed = self.core.text != text;
119 let pending_external_paste = self.pending_external_paste.take();
120 if text_changed
121 && let Some(pending) = pending_external_paste
122 && self.core.text == pending.text_before
123 {
124 self.core.sync_external_edit_from(
125 text,
126 pending.cursor_before,
127 pending.anchor_before,
128 cursor,
129 anchor,
130 );
131 } else {
132 self.core.sync_from(text, cursor, anchor);
133 }
134 if text_changed || self.core.cursor != prev_cursor {
135 self.visual_nav_col = None;
136 }
137 }
138
139 pub(crate) fn expect_external_paste_change(&mut self) {
140 self.core.normalize_cursor_and_anchor();
141 self.pending_external_paste = Some(PendingExternalPaste {
142 text_before: self.core.text().to_owned(),
143 cursor_before: self.core.cursor(),
144 anchor_before: self.core.anchor(),
145 });
146 }
147
148 pub(crate) fn remember_text_area_sentinels(
149 &mut self,
150 text: &str,
151 sentinels: &[TextAreaSentinel],
152 ) {
153 if sentinels.is_empty() {
154 return;
155 }
156
157 if let Some(pos) = self
158 .sentinel_snapshots
159 .iter()
160 .position(|snapshot| snapshot.text == text)
161 {
162 self.sentinel_snapshots.remove(pos);
163 }
164 self.sentinel_snapshots.push(SentinelSnapshot {
165 text: text.to_owned(),
166 sentinels: sentinels.to_vec(),
167 });
168 if self.sentinel_snapshots.len() > SENTINEL_SNAPSHOT_LIMIT {
169 self.sentinel_snapshots.remove(0);
170 }
171 }
172
173 pub(crate) fn remembered_text_area_sentinels(
174 &self,
175 text: &str,
176 ) -> Option<Vec<TextAreaSentinel>> {
177 self.sentinel_snapshots
178 .iter()
179 .rev()
180 .find(|snapshot| snapshot.text == text)
181 .map(|snapshot| snapshot.sentinels.clone())
182 }
183
184 pub(crate) fn remember_text_area_images(&mut self, text: &str, images: &[ImageContent]) {
185 if images.is_empty() {
186 return;
187 }
188
189 if let Some(pos) = self
190 .image_snapshots
191 .iter()
192 .position(|snapshot| snapshot.text == text)
193 {
194 self.image_snapshots.remove(pos);
195 }
196 self.image_snapshots.push(ImageSnapshot {
197 text: text.to_owned(),
198 images: images.to_vec(),
199 });
200 if self.image_snapshots.len() > SENTINEL_SNAPSHOT_LIMIT {
201 self.image_snapshots.remove(0);
202 }
203 }
204
205 pub(crate) fn remembered_text_area_images(&self, text: &str) -> Option<Vec<ImageContent>> {
206 self.image_snapshots
207 .iter()
208 .rev()
209 .find(|snapshot| snapshot.text == text)
210 .map(|snapshot| snapshot.images.clone())
211 }
212
213 pub(crate) fn insert_text(&mut self, text: &str) -> bool {
214 if text.is_empty() {
215 return false;
216 }
217 let (start, end) = self
218 .core
219 .selection()
220 .unwrap_or((self.core.cursor, self.core.cursor));
221 self.core
222 .replace_range(start, end, text, TextEditKind::Replace)
223 }
224
225 pub fn clear(&mut self) -> bool {
227 let changed = self.core.clear_preserving_history();
228 if changed {
229 self.visual_nav_col = None;
230 }
231 changed
232 }
233
234 pub fn apply(&mut self, ev: &TextAreaEvent) {
236 self.core.text = ev.value.to_string();
237 self.core.cursor = clamp_cursor(&self.core.text, ev.cursor);
238 self.core.anchor = ev
239 .anchor
240 .map(|anchor| clamp_cursor(&self.core.text, anchor));
241 }
242
243 pub fn handle_key(&mut self, key: KeyEvent) -> bool {
247 self.handle_key_with(key, crate::app::input::keymap::default_keymap())
248 }
249
250 pub(crate) fn handle_key_with(
251 &mut self,
252 key: KeyEvent,
253 keymap: &crate::app::input::keymap::Keymap,
254 ) -> bool {
255 use crate::app::input::keymap::Action;
256
257 match keymap.resolve_action(key) {
258 Action::MoveLeft => self.core.move_left(),
259 Action::MoveRight => self.core.move_right(),
260 Action::MoveUp => self.move_up(),
261 Action::MoveDown => self.move_down(),
262 Action::MoveWordLeft => self.core.move_word_left(),
263 Action::MoveWordRight => self.core.move_word_right(),
264 Action::MoveHome => self.move_home(),
265 Action::MoveEnd => self.move_end(),
266
267 Action::SelectLeft => self.core.select_left(),
268 Action::SelectRight => self.core.select_right(),
269 Action::SelectUp => self.select_up(),
270 Action::SelectDown => self.select_down(),
271 Action::SelectWordLeft => self.core.select_word_left(),
272 Action::SelectWordRight => self.core.select_word_right(),
273 Action::SelectHome => self.select_home(),
274 Action::SelectEnd => self.select_end(),
275 Action::SelectAll => self.core.select_all(),
276
277 Action::Backspace => self.core.backspace(),
278 Action::Clear => self.clear(),
279 Action::Delete => self.core.delete(),
280 Action::DeleteWordLeft => self.core.delete_word_left(),
281 Action::DeleteWordRight => self.core.delete_word_right(),
282
283 Action::Copy
284 | Action::Cut
285 | Action::Paste
286 | Action::PasteFromSelection
287 | Action::CopyImage
288 | Action::PasteImage
289 | Action::ToggleDevTools
290 | Action::Quit => false,
291 Action::Undo => self.core.undo(),
292 Action::Redo => self.core.redo(),
293
294 Action::InsertChar(c) => self.core.insert_char(c),
295 Action::InsertNewline => self.core.insert_char('\n'),
296
297 Action::DismissOverlay | Action::FocusNext | Action::FocusPrev | Action::None => false,
298 }
299 }
300
301 pub fn insert_str(&mut self, s: &str) -> bool {
303 let (start, end, kind) = if let Some((start, end)) = self.core.selection() {
304 (start, end, TextEditKind::Replace)
305 } else {
306 (self.core.cursor, self.core.cursor, TextEditKind::Insert)
307 };
308 self.core.replace_range(start, end, s, kind)
309 }
310
311 pub fn move_home(&mut self) -> bool {
315 self.core.anchor = None;
316 self.core.normalize_cursor_and_anchor();
317 let start = self.core.text[..self.core.cursor]
318 .rfind('\n')
319 .map(|i| i + 1)
320 .unwrap_or(0);
321 if self.core.cursor == start {
322 return false;
323 }
324 self.core.cursor = start;
325 true
326 }
327
328 pub fn move_end(&mut self) -> bool {
330 self.core.anchor = None;
331 self.core.normalize_cursor_and_anchor();
332 let end = self.core.text[self.core.cursor..]
333 .find('\n')
334 .map(|i| self.core.cursor + i)
335 .unwrap_or(self.core.text.len());
336 if self.core.cursor == end {
337 return false;
338 }
339 self.core.cursor = end;
340 true
341 }
342
343 pub fn select_home(&mut self) -> bool {
345 self.core.normalize_cursor_and_anchor();
346 let start = self.core.text[..self.core.cursor]
347 .rfind('\n')
348 .map(|i| i + 1)
349 .unwrap_or(0);
350 if self.core.cursor == start {
351 return false;
352 }
353 if self.core.anchor.is_none() {
354 self.core.anchor = Some(self.core.cursor);
355 }
356 self.core.cursor = start;
357 self.core.collapse_empty_selection();
358 true
359 }
360
361 pub fn select_end(&mut self) -> bool {
363 self.core.normalize_cursor_and_anchor();
364 let end = self.core.text[self.core.cursor..]
365 .find('\n')
366 .map(|i| self.core.cursor + i)
367 .unwrap_or(self.core.text.len());
368 if self.core.cursor == end {
369 return false;
370 }
371 if self.core.anchor.is_none() {
372 self.core.anchor = Some(self.core.cursor);
373 }
374 self.core.cursor = end;
375 self.core.collapse_empty_selection();
376 true
377 }
378
379 pub fn move_up(&mut self) -> bool {
383 self.core.anchor = None;
384 self.move_up_internal()
385 }
386
387 pub fn move_down(&mut self) -> bool {
389 self.core.anchor = None;
390 self.move_down_internal()
391 }
392
393 pub fn select_up(&mut self) -> bool {
395 if self.core.anchor.is_none() {
396 self.core.anchor = Some(self.core.cursor);
397 }
398 let moved = self.move_up_internal();
399 self.core.collapse_empty_selection();
400 moved
401 }
402
403 pub fn select_down(&mut self) -> bool {
405 if self.core.anchor.is_none() {
406 self.core.anchor = Some(self.core.cursor);
407 }
408 let moved = self.move_down_internal();
409 self.core.collapse_empty_selection();
410 moved
411 }
412
413 fn move_up_internal(&mut self) -> bool {
414 self.core.normalize_cursor_and_anchor();
415 let line_start = self.core.text[..self.core.cursor]
416 .rfind('\n')
417 .map(|i| i + 1)
418 .unwrap_or(0);
419 if line_start == 0 {
420 if self.core.cursor == 0 {
421 return false;
422 }
423 self.core.cursor = 0;
424 return true;
425 }
426
427 let col = self.core.text[line_start..self.core.cursor].chars().count();
428
429 let prev_line_end = line_start - 1;
430 let prev_line_start = self.core.text[..prev_line_end]
431 .rfind('\n')
432 .map(|i| i + 1)
433 .unwrap_or(0);
434
435 let prev_line = &self.core.text[prev_line_start..prev_line_end];
436 let new_col = col.min(prev_line.chars().count());
437
438 let offset: usize = prev_line.chars().take(new_col).map(|c| c.len_utf8()).sum();
439 self.core.cursor = prev_line_start + offset;
440 true
441 }
442
443 fn move_down_internal(&mut self) -> bool {
444 self.core.normalize_cursor_and_anchor();
445 let len = self.core.text.len();
446 if self.core.cursor >= len {
447 return false;
448 }
449
450 let line_end = self.core.text[self.core.cursor..]
451 .find('\n')
452 .map(|i| self.core.cursor + i);
453
454 match line_end {
455 Some(end_idx) => {
456 let line_start = self.core.text[..self.core.cursor]
457 .rfind('\n')
458 .map(|i| i + 1)
459 .unwrap_or(0);
460 let col = self.core.text[line_start..self.core.cursor].chars().count();
461
462 let next_line_start = end_idx + 1;
463 if next_line_start >= len {
464 self.core.cursor = len;
465 return true;
466 }
467
468 let next_line_end = self.core.text[next_line_start..]
469 .find('\n')
470 .map(|i| next_line_start + i)
471 .unwrap_or(len);
472
473 let next_line = &self.core.text[next_line_start..next_line_end];
474 let new_col = col.min(next_line.chars().count());
475
476 let offset: usize = next_line.chars().take(new_col).map(|c| c.len_utf8()).sum();
477 self.core.cursor = next_line_start + offset;
478 true
479 }
480 None => {
481 if self.core.cursor == len {
482 return false;
483 }
484 self.core.cursor = len;
485 true
486 }
487 }
488 }
489}
490
491#[cfg(test)]
492mod tests {
493 use super::*;
494 use crate::core::event::{KeyCode, KeyEvent, KeyMods};
495 use crate::widgets::TextAreaEvent;
496
497 fn key(code: KeyCode) -> KeyEvent {
498 KeyEvent {
499 code,
500 mods: KeyMods::default(),
501 }
502 }
503
504 fn ctrl(code: KeyCode) -> KeyEvent {
505 KeyEvent {
506 code,
507 mods: KeyMods {
508 ctrl: true,
509 ..KeyMods::default()
510 },
511 }
512 }
513
514 #[test]
515 fn undo_redo_multiline() {
516 let mut editor = TextEditor::new("");
517 assert!(editor.handle_key(key(KeyCode::Char('a'))));
518 assert!(editor.handle_key(key(KeyCode::Enter)));
519 assert!(editor.handle_key(key(KeyCode::Char('b'))));
520 assert_eq!(editor.text(), "a\nb");
521
522 assert!(editor.handle_key(ctrl(KeyCode::Char('z'))));
523 assert_eq!(editor.text(), "a\n");
524
525 assert!(editor.handle_key(ctrl(KeyCode::Char('y'))));
526 assert_eq!(editor.text(), "a\nb");
527 }
528
529 #[test]
530 fn undo_redo_word_merges() {
531 let mut editor = TextEditor::new("");
532 assert!(editor.handle_key(key(KeyCode::Char('a'))));
533 assert!(editor.handle_key(key(KeyCode::Char('b'))));
534 assert!(editor.handle_key(key(KeyCode::Char('c'))));
535 assert_eq!(editor.text(), "abc");
536
537 assert!(editor.handle_key(ctrl(KeyCode::Char('z'))));
538 assert_eq!(editor.text(), "");
539
540 assert!(editor.handle_key(ctrl(KeyCode::Char('y'))));
541 assert_eq!(editor.text(), "abc");
542 }
543
544 #[test]
545 fn undo_merges_trailing_space() {
546 let mut editor = TextEditor::new("");
547 assert!(editor.handle_key(key(KeyCode::Char('a'))));
548 assert!(editor.handle_key(key(KeyCode::Char('b'))));
549 assert!(editor.handle_key(key(KeyCode::Char(' '))));
550 assert_eq!(editor.text(), "ab ");
551
552 assert!(editor.handle_key(ctrl(KeyCode::Char('z'))));
553 assert_eq!(editor.text(), "");
554
555 assert!(editor.handle_key(ctrl(KeyCode::Char('y'))));
556 assert_eq!(editor.text(), "ab ");
557 }
558
559 #[test]
560 fn move_down_and_up_through_lines() {
561 let mut editor = TextEditor::new("abc\ndef\nghi");
562 assert_eq!(editor.cursor(), 0);
564
565 assert!(editor.move_down());
567 assert!(editor.move_down());
568
569 assert!(editor.move_up());
571 assert!(editor.move_up());
572 assert_eq!(editor.cursor(), 0);
573 }
574
575 #[test]
576 fn move_down_clamps_column_to_shorter_line() {
577 let mut editor = TextEditor::new("abcde\nab\nabcde");
579 editor.set_cursor(5);
581 assert_eq!(editor.cursor(), 5);
582
583 assert!(editor.move_down());
585 assert_eq!(editor.cursor(), 8); assert!(editor.move_down());
589 assert_eq!(editor.cursor(), 11);
590 }
591
592 #[test]
593 fn move_up_on_first_line_moves_to_line_start() {
594 let mut editor = TextEditor::new("abc\ndef");
595 editor.set_cursor(2); assert!(editor.move_up());
597 assert_eq!(editor.cursor(), 0);
598 assert!(!editor.move_up());
599 }
600
601 #[test]
602 fn move_down_on_last_line_moves_to_line_end() {
603 let mut editor = TextEditor::new("abc\ndef");
604 editor.set_cursor(5); assert!(editor.move_down());
607 assert_eq!(editor.cursor(), 7);
608 assert!(!editor.move_down());
609 }
610
611 #[test]
612 fn select_up_down_at_boundaries_select_line_edge() {
613 let mut editor = TextEditor::new("abc\ndef");
614 editor.set_cursor(2);
615 assert!(editor.select_up());
616 assert_eq!(editor.cursor(), 0);
617 assert_eq!(editor.selection(), Some((0, 2)));
618
619 editor.set_cursor(5);
620 assert!(editor.select_down());
621 assert_eq!(editor.cursor(), 7);
622 assert_eq!(editor.selection(), Some((5, 7)));
623 }
624
625 #[test]
626 fn move_home_goes_to_line_start() {
627 let mut editor = TextEditor::new("abc\ndef");
628 editor.set_cursor(5);
630 assert!(editor.move_home());
631 assert_eq!(editor.cursor(), 4);
633 }
634
635 #[test]
636 fn move_end_goes_to_line_end() {
637 let mut editor = TextEditor::new("abc\ndef");
638 editor.set_cursor(4);
640 assert!(editor.move_end());
641 assert_eq!(editor.cursor(), 7);
643 }
644
645 #[test]
646 fn backspace_at_line_start_joins_lines() {
647 let mut editor = TextEditor::new("abc\ndef");
648 editor.set_cursor(4);
650 assert!(editor.backspace());
651 assert_eq!(editor.text(), "abcdef");
652 assert_eq!(editor.cursor(), 3); }
654
655 #[test]
656 fn delete_at_line_end_joins_lines() {
657 let mut editor = TextEditor::new("abc\ndef");
658 editor.set_cursor(3);
660 assert!(editor.delete());
661 assert_eq!(editor.text(), "abcdef");
662 assert_eq!(editor.cursor(), 3); }
664
665 #[test]
666 fn insert_newline_splits_line() {
667 let mut editor = TextEditor::new("abcdef");
668 editor.set_cursor(3);
669 assert!(editor.insert_char('\n'));
670 assert_eq!(editor.text(), "abc\ndef");
671 assert_eq!(editor.cursor(), 4); }
673
674 #[test]
675 fn select_down_creates_vertical_selection() {
676 let mut editor = TextEditor::new("abc\ndef");
677 editor.set_cursor(1);
679 assert!(editor.select_down());
680 assert_eq!(editor.anchor(), Some(1));
682 assert_eq!(editor.cursor(), 5);
684 }
685
686 #[test]
687 fn empty_lines_traversal() {
688 let mut editor = TextEditor::new("a\n\nb");
689 assert_eq!(editor.cursor(), 0);
691
692 assert!(editor.move_down());
694 assert_eq!(editor.cursor(), 2);
695
696 assert!(editor.move_down());
698 assert_eq!(editor.cursor(), 3);
699
700 assert!(editor.move_up());
702 assert_eq!(editor.cursor(), 2);
703
704 assert!(editor.move_up());
706 assert_eq!(editor.cursor(), 0);
707 }
708
709 #[test]
710 fn toggle_devtools_action_is_non_editing() {
711 let mut editor = TextEditor::new("hello");
712 let changed = editor.handle_key(KeyEvent {
713 code: KeyCode::F(12),
714 mods: KeyMods::default(),
715 });
716
717 assert!(!changed);
718 assert_eq!(editor.text(), "hello");
719 assert_eq!(editor.cursor(), 0);
720 }
721
722 #[test]
723 fn multi_line_selection_delete() {
724 let mut editor = TextEditor::new("abc\ndef\nghi");
725 editor.set_cursor(1);
727 editor.set_anchor(Some(8));
728 assert!(editor.delete_selection());
729 assert_eq!(editor.text(), "aghi");
730 assert_eq!(editor.cursor(), 1);
731 assert_eq!(editor.anchor(), None);
732 }
733
734 #[test]
735 fn apply_updates_editor_after_insert() {
736 let mut editor = TextEditor::new("abc");
737 TextAreaEvent {
738 value: "abc\ndef".into(),
739 cursor: 7,
740 anchor: None,
741 }
742 .apply_to(&mut editor);
743
744 assert_eq!(editor.text(), "abc\ndef");
745 assert_eq!(editor.cursor(), 7);
746 assert_eq!(editor.anchor(), None);
747 }
748
749 #[test]
750 fn apply_updates_editor_after_delete() {
751 let mut editor = TextEditor::new("abc\ndef");
752 TextAreaEvent {
753 value: "abcdef".into(),
754 cursor: 3,
755 anchor: None,
756 }
757 .apply_to(&mut editor);
758
759 assert_eq!(editor.text(), "abcdef");
760 assert_eq!(editor.cursor(), 3);
761 assert_eq!(editor.anchor(), None);
762 }
763
764 #[test]
765 fn apply_updates_cursor_navigation_without_text_change() {
766 let mut editor = TextEditor::new("abc\ndef");
767 TextAreaEvent {
768 value: "abc\ndef".into(),
769 cursor: 5,
770 anchor: None,
771 }
772 .apply_to(&mut editor);
773
774 assert_eq!(editor.text(), "abc\ndef");
775 assert_eq!(editor.cursor(), 5);
776 assert_eq!(editor.anchor(), None);
777 }
778
779 #[test]
780 fn apply_updates_selection_anchor() {
781 let mut editor = TextEditor::new("abc\ndef");
782 TextAreaEvent {
783 value: "abc\ndef".into(),
784 cursor: 6,
785 anchor: Some(1),
786 }
787 .apply_to(&mut editor);
788
789 assert_eq!(editor.selection(), Some((1, 6)));
790 }
791
792 #[test]
793 fn apply_clamps_cursor_and_anchor_inside_unicode_characters() {
794 let value = "części\newaluacyjnej";
795 let cursor_inside_s = 5;
796 assert!(!value.is_char_boundary(cursor_inside_s));
797
798 let mut editor = TextEditor::new("");
799 TextAreaEvent {
800 value: value.into(),
801 cursor: cursor_inside_s,
802 anchor: Some(cursor_inside_s),
803 }
804 .apply_to(&mut editor);
805
806 assert_eq!(editor.cursor(), 4);
807 assert_eq!(editor.anchor(), Some(4));
808 assert!(editor.text().is_char_boundary(editor.cursor()));
809 }
810
811 #[test]
812 fn line_navigation_tolerates_unicode_boundary_drift() {
813 let mut editor = TextEditor::new("abc\nczęści\nxyz");
814 editor.core.cursor = 9;
815 assert!(!editor.text().is_char_boundary(editor.core.cursor));
816
817 assert!(editor.move_end());
818 assert_eq!(editor.cursor(), "abc\nczęści".len());
819
820 editor.core.cursor = 9;
821 assert!(editor.move_home());
822 assert_eq!(editor.cursor(), "abc\n".len());
823
824 editor.core.cursor = 9;
825 assert!(editor.move_down());
826 assert!(editor.text().is_char_boundary(editor.cursor()));
827 }
828
829 #[test]
830 fn clear_is_undoable_and_redoable_with_cursor_and_anchor() {
831 let mut editor = TextEditor::new("abc\ndef");
832 editor.set_cursor(6);
833 editor.set_anchor(Some(1));
834 editor.set_visual_nav_col(Some(3));
835
836 assert!(editor.clear());
837 assert_eq!(editor.text(), "");
838 assert_eq!(editor.cursor(), 0);
839 assert_eq!(editor.anchor(), None);
840 assert_eq!(editor.visual_nav_col(), None);
841
842 assert!(editor.undo());
843 assert_eq!(editor.text(), "abc\ndef");
844 assert_eq!(editor.cursor(), 6);
845 assert_eq!(editor.anchor(), Some(1));
846
847 assert!(editor.redo());
848 assert_eq!(editor.text(), "");
849 assert_eq!(editor.cursor(), 0);
850 assert_eq!(editor.anchor(), None);
851 }
852
853 #[test]
854 fn clear_empty_text_only_normalizes_cursor_and_anchor() {
855 let mut editor = TextEditor::new("");
856 editor.set_cursor_keep_anchor(10);
857 editor.set_anchor(Some(10));
858
859 assert!(editor.clear());
860 assert_eq!(editor.cursor(), 0);
861 assert_eq!(editor.anchor(), None);
862 assert!(!editor.can_undo());
863 assert!(!editor.clear());
864 }
865}