1use alux_ext::ext;
2use ambassador::delegatable_trait;
3
4#[delegatable_trait]
6pub trait TextEditorStateAlg {
7 fn active_text_editor(&self) -> Option<(&str, usize)>;
9 fn set_active_text_editor(&mut self, text: String, cursor: usize);
11}
12
13#[ext(name = TextCursorExt)]
15pub impl<This> This
16where
17 This: TextEditorStateAlg,
18{
19 fn insert_text(&mut self, insertion: &str) {
21 let Some((text, cursor)) = self.active_text_editor() else {
22 return;
23 };
24 let mut edited = text.to_owned();
25 edited.insert_str(cursor, insertion);
26 self.set_active_text_editor(edited, cursor.saturating_add(insertion.len()));
27 }
28
29 fn delete_before_cursor(&mut self) {
31 let Some((text, cursor)) = self.active_text_editor() else {
32 return;
33 };
34 let previous = previous_boundary(text, cursor);
35 let mut edited = text.to_owned();
36 edited.drain(previous..cursor);
37 self.set_active_text_editor(edited, previous);
38 }
39
40 fn delete_at_cursor(&mut self) {
42 let Some((text, cursor)) = self.active_text_editor() else {
43 return;
44 };
45 let next = next_boundary(text, cursor);
46 let mut edited = text.to_owned();
47 edited.drain(cursor..next);
48 self.set_active_text_editor(edited, cursor);
49 }
50
51 fn move_cursor_left(&mut self) {
53 let Some((text, cursor)) = self.active_text_editor() else {
54 return;
55 };
56 self.set_active_text_editor(text.to_owned(), previous_boundary(text, cursor));
57 }
58
59 fn move_cursor_right(&mut self) {
61 let Some((text, cursor)) = self.active_text_editor() else {
62 return;
63 };
64 self.set_active_text_editor(text.to_owned(), next_boundary(text, cursor));
65 }
66
67 fn move_cursor_home(&mut self) {
69 let Some((text, cursor)) = self.active_text_editor() else {
70 return;
71 };
72 let home = text[..cursor].rfind('\n').map_or(0, |index| index + 1);
73 self.set_active_text_editor(text.to_owned(), home);
74 }
75
76 fn move_cursor_end(&mut self) {
78 let Some((text, cursor)) = self.active_text_editor() else {
79 return;
80 };
81 let end = text[cursor..].find('\n').map_or(text.len(), |index| cursor + index);
82 self.set_active_text_editor(text.to_owned(), end);
83 }
84}
85
86fn previous_boundary(text: &str, cursor: usize) -> usize {
87 text[..cursor].char_indices().next_back().map_or(0, |(index, _)| index)
88}
89
90fn next_boundary(text: &str, cursor: usize) -> usize {
91 text[cursor..].chars().next().map_or(cursor, |character| cursor + character.len_utf8())
92}