freya_edit/
rope_editor.rs

1use std::{
2    cmp::Ordering,
3    fmt::Display,
4    ops::Range,
5};
6
7use ropey::{
8    Rope,
9    iter::Lines,
10};
11
12use crate::{
13    EditorLine,
14    TextSelection,
15    editor_history::{
16        EditorHistory,
17        HistoryChange,
18    },
19    text_editor::{
20        Line,
21        TextEditor,
22    },
23};
24
25/// TextEditor implementing a Rope
26pub struct RopeEditor {
27    pub(crate) rope: Rope,
28    pub(crate) selection: TextSelection,
29    pub(crate) indentation: u8,
30    pub(crate) history: EditorHistory,
31}
32
33impl Display for RopeEditor {
34    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
35        f.write_str(&self.rope.to_string())
36    }
37}
38
39impl RopeEditor {
40    // Create a new [`RopeEditor`]
41    pub fn new(
42        text: String,
43        selection: TextSelection,
44        indentation: u8,
45        history: EditorHistory,
46    ) -> Self {
47        Self {
48            rope: Rope::from_str(&text),
49            selection,
50            indentation,
51            history,
52        }
53    }
54
55    pub fn rope(&self) -> &Rope {
56        &self.rope
57    }
58}
59
60impl TextEditor for RopeEditor {
61    type LinesIterator<'a> = LinesIterator<'a>;
62
63    fn lines(&self) -> Self::LinesIterator<'_> {
64        let lines = self.rope.lines();
65        LinesIterator { lines }
66    }
67
68    fn insert_char(&mut self, ch: char, idx: usize) -> usize {
69        let idx_utf8 = self.utf16_cu_to_char(idx);
70        let selection = self.selection.clone();
71
72        let len_before_insert = self.rope.len_utf16_cu();
73        self.rope.insert_char(idx_utf8, ch);
74        let len_after_insert = self.rope.len_utf16_cu();
75
76        let inserted_text_len = len_after_insert - len_before_insert;
77
78        self.history.push_change(HistoryChange::InsertChar {
79            idx,
80            ch,
81            len: inserted_text_len,
82            selection,
83        });
84
85        inserted_text_len
86    }
87
88    fn insert(&mut self, text: &str, idx: usize) -> usize {
89        let idx_utf8 = self.utf16_cu_to_char(idx);
90        let selection = self.selection.clone();
91
92        let len_before_insert = self.rope.len_utf16_cu();
93        self.rope.insert(idx_utf8, text);
94        let len_after_insert = self.rope.len_utf16_cu();
95
96        let inserted_text_len = len_after_insert - len_before_insert;
97
98        self.history.push_change(HistoryChange::InsertText {
99            idx,
100            text: text.to_owned(),
101            len: inserted_text_len,
102            selection,
103        });
104
105        inserted_text_len
106    }
107
108    fn remove(&mut self, range_utf16: Range<usize>) -> usize {
109        let range =
110            self.utf16_cu_to_char(range_utf16.start)..self.utf16_cu_to_char(range_utf16.end);
111        let text = self.rope.slice(range.clone()).to_string();
112        let selection = self.selection.clone();
113
114        let len_before_remove = self.rope.len_utf16_cu();
115        self.rope.remove(range);
116        let len_after_remove = self.rope.len_utf16_cu();
117
118        let removed_text_len = len_before_remove - len_after_remove;
119
120        self.history.push_change(HistoryChange::Remove {
121            idx: range_utf16.end - removed_text_len,
122            text,
123            len: removed_text_len,
124            selection,
125        });
126
127        removed_text_len
128    }
129
130    fn char_to_line(&self, char_idx: usize) -> usize {
131        self.rope.char_to_line(char_idx)
132    }
133
134    fn line_to_char(&self, line_idx: usize) -> usize {
135        self.rope.line_to_char(line_idx)
136    }
137
138    fn utf16_cu_to_char(&self, utf16_cu_idx: usize) -> usize {
139        self.rope.utf16_cu_to_char(utf16_cu_idx)
140    }
141
142    fn char_to_utf16_cu(&self, idx: usize) -> usize {
143        self.rope.char_to_utf16_cu(idx)
144    }
145
146    fn line(&self, line_idx: usize) -> Option<Line<'_>> {
147        let line = self.rope.get_line(line_idx);
148
149        line.map(|line| Line {
150            text: line.into(),
151            utf16_len: line.len_utf16_cu(),
152        })
153    }
154
155    fn len_lines(&self) -> usize {
156        self.rope.len_lines()
157    }
158
159    fn len_chars(&self) -> usize {
160        self.rope.len_chars()
161    }
162
163    fn len_utf16_cu(&self) -> usize {
164        self.rope.len_utf16_cu()
165    }
166
167    fn selection(&self) -> &TextSelection {
168        &self.selection
169    }
170
171    fn selection_mut(&mut self) -> &mut TextSelection {
172        &mut self.selection
173    }
174
175    fn has_any_selection(&self) -> bool {
176        self.selection.is_range()
177    }
178
179    fn get_selection(&self) -> Option<(usize, usize)> {
180        match self.selection {
181            TextSelection::Cursor(_) => None,
182            TextSelection::Range { from, to } => Some((from, to)),
183        }
184    }
185
186    fn get_visible_selection(&self, editor_line: EditorLine) -> Option<(usize, usize)> {
187        let (selected_from, selected_to) = match self.selection {
188            TextSelection::Cursor(_) => return None,
189            TextSelection::Range { from, to } => (from, to),
190        };
191
192        match editor_line {
193            EditorLine::Paragraph(line_index) => {
194                let selected_from_row = self.char_to_line(self.utf16_cu_to_char(selected_from));
195                let selected_to_row = self.char_to_line(self.utf16_cu_to_char(selected_to));
196
197                let editor_row_idx = self.char_to_utf16_cu(self.line_to_char(line_index));
198                let selected_from_row_idx =
199                    self.char_to_utf16_cu(self.line_to_char(selected_from_row));
200                let selected_to_row_idx = self.char_to_utf16_cu(self.line_to_char(selected_to_row));
201
202                let selected_from_col_idx = selected_from - selected_from_row_idx;
203                let selected_to_col_idx = selected_to - selected_to_row_idx;
204
205                // Between starting line and endling line
206                if (line_index > selected_from_row && line_index < selected_to_row)
207                    || (line_index < selected_from_row && line_index > selected_to_row)
208                {
209                    let len = self.line(line_index).unwrap().utf16_len();
210                    return Some((0, len));
211                }
212
213                match selected_from_row.cmp(&selected_to_row) {
214                    // Selection direction is from bottom -> top
215                    Ordering::Greater => {
216                        if selected_from_row == line_index {
217                            // Starting line
218                            Some((0, selected_from_col_idx))
219                        } else if selected_to_row == line_index {
220                            // Ending line
221                            let len = self.line(selected_to_row).unwrap().utf16_len();
222                            Some((selected_to_col_idx, len))
223                        } else {
224                            None
225                        }
226                    }
227                    // Selection direction is from top -> bottom
228                    Ordering::Less => {
229                        if selected_from_row == line_index {
230                            // Starting line
231                            let len = self.line(selected_from_row).unwrap().utf16_len();
232                            Some((selected_from_col_idx, len))
233                        } else if selected_to_row == line_index {
234                            // Ending line
235                            Some((0, selected_to_col_idx))
236                        } else {
237                            None
238                        }
239                    }
240                    Ordering::Equal if selected_from_row == line_index => {
241                        // Starting and endline line are the same
242                        Some((selected_from - editor_row_idx, selected_to - editor_row_idx))
243                    }
244                    _ => None,
245                }
246            }
247            EditorLine::SingleParagraph => Some((selected_from, selected_to)),
248        }
249    }
250
251    fn set(&mut self, text: &str) {
252        self.rope.remove(0..);
253        self.rope.insert(0, text);
254        if self.cursor_pos() > text.len() {
255            self.move_cursor_to(text.len());
256        }
257    }
258
259    fn clear_selection(&mut self) {
260        let end = self.selection().end();
261        self.selection_mut().set_as_cursor();
262        self.selection_mut().move_to(end);
263    }
264
265    fn set_selection(&mut self, (from, to): (usize, usize)) {
266        self.selection = TextSelection::Range { from, to };
267    }
268
269    fn get_selected_text(&self) -> Option<String> {
270        let (start, end) = self.get_selection_range()?;
271
272        let start = self.utf16_cu_to_char(start);
273        let end = self.utf16_cu_to_char(end);
274
275        Some(self.rope().get_slice(start..end)?.to_string())
276    }
277
278    fn get_selection_range(&self) -> Option<(usize, usize)> {
279        let (start, end) = match self.selection {
280            TextSelection::Cursor(_) => return None,
281            TextSelection::Range { from, to } => (from, to),
282        };
283
284        // Use left-to-right selection
285        let (start, end) = if start < end {
286            (start, end)
287        } else {
288            (end, start)
289        };
290
291        Some((start, end))
292    }
293
294    fn undo(&mut self) -> Option<TextSelection> {
295        self.history.undo(&mut self.rope)
296    }
297
298    fn redo(&mut self) -> Option<TextSelection> {
299        self.history.redo(&mut self.rope)
300    }
301
302    fn editor_history(&mut self) -> &mut EditorHistory {
303        &mut self.history
304    }
305
306    fn get_indentation(&self) -> u8 {
307        self.indentation
308    }
309}
310
311/// Iterator over text lines.
312pub struct LinesIterator<'a> {
313    pub lines: Lines<'a>,
314}
315
316impl<'a> Iterator for LinesIterator<'a> {
317    type Item = Line<'a>;
318
319    fn next(&mut self) -> Option<Self::Item> {
320        let line = self.lines.next();
321
322        line.map(|line| Line {
323            text: line.into(),
324            utf16_len: line.len_utf16_cu(),
325        })
326    }
327}