freya_edit/
rope_editor.rs

1use std::{
2    fmt::Display,
3    ops::Range,
4};
5
6use ropey::{
7    Rope,
8    iter::Lines,
9};
10
11use crate::{
12    TextSelection,
13    editor_history::{
14        EditorHistory,
15        HistoryChange,
16    },
17    text_editor::{
18        Line,
19        TextEditor,
20    },
21};
22
23/// TextEditor implementing a Rope
24pub struct RopeEditor {
25    pub(crate) rope: Rope,
26    pub(crate) selection: TextSelection,
27    pub(crate) indentation: u8,
28    pub(crate) history: EditorHistory,
29}
30
31impl Display for RopeEditor {
32    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
33        f.write_str(&self.rope.to_string())
34    }
35}
36
37impl RopeEditor {
38    // Create a new [`RopeEditor`]
39    pub fn new(
40        text: String,
41        selection: TextSelection,
42        indentation: u8,
43        history: EditorHistory,
44    ) -> Self {
45        Self {
46            rope: Rope::from_str(&text),
47            selection,
48            indentation,
49            history,
50        }
51    }
52
53    pub fn rope(&self) -> &Rope {
54        &self.rope
55    }
56}
57
58impl TextEditor for RopeEditor {
59    type LinesIterator<'a> = LinesIterator<'a>;
60
61    fn lines(&self) -> Self::LinesIterator<'_> {
62        let lines = self.rope.lines();
63        LinesIterator { lines }
64    }
65
66    fn insert_char(&mut self, ch: char, idx: usize) -> usize {
67        let idx_utf8 = self.utf16_cu_to_char(idx);
68        let selection = self.selection.clone();
69
70        let len_before_insert = self.rope.len_utf16_cu();
71        self.rope.insert_char(idx_utf8, ch);
72        let len_after_insert = self.rope.len_utf16_cu();
73
74        let inserted_text_len = len_after_insert - len_before_insert;
75
76        self.history.push_change(HistoryChange::InsertChar {
77            idx,
78            ch,
79            len: inserted_text_len,
80            selection,
81        });
82
83        inserted_text_len
84    }
85
86    fn insert(&mut self, text: &str, idx: usize) -> usize {
87        let idx_utf8 = self.utf16_cu_to_char(idx);
88        let selection = self.selection.clone();
89
90        let len_before_insert = self.rope.len_utf16_cu();
91        self.rope.insert(idx_utf8, text);
92        let len_after_insert = self.rope.len_utf16_cu();
93
94        let inserted_text_len = len_after_insert - len_before_insert;
95
96        self.history.push_change(HistoryChange::InsertText {
97            idx,
98            text: text.to_owned(),
99            len: inserted_text_len,
100            selection,
101        });
102
103        inserted_text_len
104    }
105
106    fn remove(&mut self, range_utf16: Range<usize>) -> usize {
107        let range =
108            self.utf16_cu_to_char(range_utf16.start)..self.utf16_cu_to_char(range_utf16.end);
109        let text = self.rope.slice(range.clone()).to_string();
110        let selection = self.selection.clone();
111
112        let len_before_remove = self.rope.len_utf16_cu();
113        self.rope.remove(range);
114        let len_after_remove = self.rope.len_utf16_cu();
115
116        let removed_text_len = len_before_remove - len_after_remove;
117
118        self.history.push_change(HistoryChange::Remove {
119            idx: range_utf16.end - removed_text_len,
120            text,
121            len: removed_text_len,
122            selection,
123        });
124
125        removed_text_len
126    }
127
128    fn char_to_line(&self, char_idx: usize) -> usize {
129        self.rope.char_to_line(char_idx)
130    }
131
132    fn line_to_char(&self, line_idx: usize) -> usize {
133        self.rope.line_to_char(line_idx)
134    }
135
136    fn utf16_cu_to_char(&self, utf16_cu_idx: usize) -> usize {
137        self.rope.utf16_cu_to_char(utf16_cu_idx)
138    }
139
140    fn char_to_utf16_cu(&self, idx: usize) -> usize {
141        self.rope.char_to_utf16_cu(idx)
142    }
143
144    fn line(&self, line_idx: usize) -> Option<Line<'_>> {
145        let line = self.rope.get_line(line_idx);
146
147        line.map(|line| Line {
148            text: line.into(),
149            utf16_len: line.len_utf16_cu(),
150        })
151    }
152
153    fn len_lines(&self) -> usize {
154        self.rope.len_lines()
155    }
156
157    fn len_chars(&self) -> usize {
158        self.rope.len_chars()
159    }
160
161    fn len_utf16_cu(&self) -> usize {
162        self.rope.len_utf16_cu()
163    }
164
165    fn selection(&self) -> &TextSelection {
166        &self.selection
167    }
168
169    fn selection_mut(&mut self) -> &mut TextSelection {
170        &mut self.selection
171    }
172
173    fn has_any_selection(&self) -> bool {
174        self.selection.is_range()
175    }
176
177    fn get_selection(&self) -> Option<(usize, usize)> {
178        match self.selection {
179            TextSelection::Cursor(_) => None,
180            TextSelection::Range { from, to } => Some((from, to)),
181        }
182    }
183
184    fn set(&mut self, text: &str) {
185        self.rope.remove(0..);
186        self.rope.insert(0, text);
187        if self.cursor_pos() > text.len() {
188            self.move_cursor_to(text.len());
189        }
190    }
191
192    fn clear_selection(&mut self) {
193        let end = self.selection().end();
194        self.selection_mut().set_as_cursor();
195        self.selection_mut().move_to(end);
196    }
197
198    fn set_selection(&mut self, (from, to): (usize, usize)) {
199        self.selection = TextSelection::Range { from, to };
200    }
201
202    fn get_selected_text(&self) -> Option<String> {
203        let (start, end) = self.get_selection_range()?;
204
205        let start = self.utf16_cu_to_char(start);
206        let end = self.utf16_cu_to_char(end);
207
208        Some(self.rope().get_slice(start..end)?.to_string())
209    }
210
211    fn get_selection_range(&self) -> Option<(usize, usize)> {
212        let (start, end) = match self.selection {
213            TextSelection::Cursor(_) => return None,
214            TextSelection::Range { from, to } => (from, to),
215        };
216
217        // Use left-to-right selection
218        let (start, end) = if start < end {
219            (start, end)
220        } else {
221            (end, start)
222        };
223
224        Some((start, end))
225    }
226
227    fn undo(&mut self) -> Option<TextSelection> {
228        self.history.undo(&mut self.rope)
229    }
230
231    fn redo(&mut self) -> Option<TextSelection> {
232        self.history.redo(&mut self.rope)
233    }
234
235    fn editor_history(&mut self) -> &mut EditorHistory {
236        &mut self.history
237    }
238
239    fn get_indentation(&self) -> u8 {
240        self.indentation
241    }
242}
243
244/// Iterator over text lines.
245pub struct LinesIterator<'a> {
246    pub lines: Lines<'a>,
247}
248
249impl<'a> Iterator for LinesIterator<'a> {
250    type Item = Line<'a>;
251
252    fn next(&mut self) -> Option<Self::Item> {
253        let line = self.lines.next();
254
255        line.map(|line| Line {
256            text: line.into(),
257            utf16_len: line.len_utf16_cu(),
258        })
259    }
260}