1use std::{
2 cmp::Ordering,
3 fmt::Display,
4 ops::Range,
5};
6
7use dioxus_clipboard::prelude::UseClipboard;
8use ropey::iter::Lines;
9pub use ropey::Rope;
10
11use crate::{
12 text_editor::*,
13 EditableMode,
14 EditorHistory,
15 HistoryChange,
16};
17
18pub struct RopeEditor {
20 pub(crate) rope: Rope,
21 pub(crate) cursor: TextCursor,
22 pub(crate) identation: u8,
23 pub(crate) mode: EditableMode,
24 pub(crate) selected: Option<(usize, usize)>,
25 pub(crate) clipboard: UseClipboard,
26 pub(crate) history: EditorHistory,
27}
28
29impl Display for RopeEditor {
30 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
31 f.write_str(&self.rope.to_string())
32 }
33}
34
35impl RopeEditor {
36 pub fn new(
38 text: String,
39 cursor: TextCursor,
40 identation: u8,
41 mode: EditableMode,
42 clipboard: UseClipboard,
43 history: EditorHistory,
44 ) -> Self {
45 Self {
46 rope: Rope::from_str(&text),
47 cursor,
48 identation,
49 selected: None,
50 mode,
51 clipboard,
52 history,
53 }
54 }
55
56 pub fn rope(&self) -> &Rope {
57 &self.rope
58 }
59}
60
61impl TextEditor for RopeEditor {
62 type LinesIterator<'a> = LinesIterator<'a>;
63
64 fn lines(&self) -> Self::LinesIterator<'_> {
65 let lines = self.rope.lines();
66 LinesIterator { lines }
67 }
68
69 fn insert_char(&mut self, ch: char, idx: usize) -> usize {
70 let idx_utf8 = self.utf16_cu_to_char(idx);
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 });
83
84 inserted_text_len
85 }
86
87 fn insert(&mut self, text: &str, idx: usize) -> usize {
88 let idx_utf8 = self.utf16_cu_to_char(idx);
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 });
101
102 inserted_text_len
103 }
104
105 fn remove(&mut self, range_utf16: Range<usize>) -> usize {
106 let range =
107 self.utf16_cu_to_char(range_utf16.start)..self.utf16_cu_to_char(range_utf16.end);
108 let text = self.rope.slice(range.clone()).to_string();
109
110 let len_before_remove = self.rope.len_utf16_cu();
111 self.rope.remove(range);
112 let len_after_remove = self.rope.len_utf16_cu();
113
114 let removed_text_len = len_before_remove - len_after_remove;
115
116 self.history.push_change(HistoryChange::Remove {
117 idx: range_utf16.end - removed_text_len,
118 text,
119 len: removed_text_len,
120 });
121
122 removed_text_len
123 }
124
125 fn char_to_line(&self, char_idx: usize) -> usize {
126 self.rope.char_to_line(char_idx)
127 }
128
129 fn line_to_char(&self, line_idx: usize) -> usize {
130 self.rope.line_to_char(line_idx)
131 }
132
133 fn utf16_cu_to_char(&self, utf16_cu_idx: usize) -> usize {
134 self.rope.utf16_cu_to_char(utf16_cu_idx)
135 }
136
137 fn char_to_utf16_cu(&self, idx: usize) -> usize {
138 self.rope.char_to_utf16_cu(idx)
139 }
140
141 fn line(&self, line_idx: usize) -> Option<Line<'_>> {
142 let line = self.rope.get_line(line_idx);
143
144 line.map(|line| Line {
145 text: line.into(),
146 utf16_len: line.len_utf16_cu(),
147 })
148 }
149
150 fn len_lines(&self) -> usize {
151 self.rope.len_lines()
152 }
153
154 fn len_chars(&self) -> usize {
155 self.rope.len_chars()
156 }
157
158 fn len_utf16_cu(&self) -> usize {
159 self.rope.len_utf16_cu()
160 }
161
162 fn cursor(&self) -> &TextCursor {
163 &self.cursor
164 }
165
166 fn cursor_mut(&mut self) -> &mut TextCursor {
167 &mut self.cursor
168 }
169
170 fn expand_selection_to_cursor(&mut self) {
171 let pos = self.cursor_pos();
172 if let Some(selected) = self.selected.as_mut() {
173 selected.1 = pos;
174 } else {
175 self.selected = Some((self.cursor_pos(), self.cursor_pos()))
176 }
177 }
178
179 fn get_clipboard(&mut self) -> &mut UseClipboard {
180 &mut self.clipboard
181 }
182
183 fn has_any_selection(&self) -> bool {
184 self.selected.is_some()
185 }
186
187 fn get_selection(&self) -> Option<(usize, usize)> {
188 self.selected
189 }
190
191 fn get_visible_selection(&self, editor_id: usize) -> Option<(usize, usize)> {
192 let (selected_from, selected_to) = self.selected?;
193
194 if self.mode == EditableMode::SingleLineMultipleEditors {
195 let selected_from_row = self.char_to_line(self.utf16_cu_to_char(selected_from));
196 let selected_to_row = self.char_to_line(self.utf16_cu_to_char(selected_to));
197
198 let editor_row_idx = self.char_to_utf16_cu(self.line_to_char(editor_id));
199 let selected_from_row_idx = 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 if (editor_id > selected_from_row && editor_id < selected_to_row)
207 || (editor_id < selected_from_row && editor_id > selected_to_row)
208 {
209 let len = self.line(editor_id).unwrap().utf16_len();
210 return Some((0, len));
211 }
212
213 let highlights = match selected_from_row.cmp(&selected_to_row) {
214 Ordering::Greater => {
216 if selected_from_row == editor_id {
217 Some((0, selected_from_col_idx))
219 } else if selected_to_row == editor_id {
220 let len = self.line(selected_to_row).unwrap().utf16_len();
222 Some((selected_to_col_idx, len))
223 } else {
224 None
225 }
226 }
227 Ordering::Less => {
229 if selected_from_row == editor_id {
230 let len = self.line(selected_from_row).unwrap().utf16_len();
232 Some((selected_from_col_idx, len))
233 } else if selected_to_row == editor_id {
234 Some((0, selected_to_col_idx))
236 } else {
237 None
238 }
239 }
240 Ordering::Equal if selected_from_row == editor_id => {
241 Some((selected_from - editor_row_idx, selected_to - editor_row_idx))
243 }
244 _ => None,
245 };
246
247 highlights
248 } else {
249 Some((selected_from, selected_to))
250 }
251 }
252
253 fn set(&mut self, text: &str) {
254 self.rope.remove(0..);
255 self.rope.insert(0, text);
256 if self.cursor_pos() > text.len() {
257 self.set_cursor_pos(text.len());
258 }
259 }
260
261 fn clear_selection(&mut self) {
262 self.selected = None;
263 }
264
265 fn measure_new_selection(&self, from: usize, to: usize, editor_id: usize) -> (usize, usize) {
266 if self.mode == EditableMode::SingleLineMultipleEditors {
267 let row_idx = self.line_to_char(editor_id);
268 let row_idx = self.char_to_utf16_cu(row_idx);
269 if let Some((start, _)) = self.selected {
270 (start, row_idx + to)
271 } else {
272 (row_idx + from, row_idx + to)
273 }
274 } else if let Some((start, _)) = self.selected {
275 (start, to)
276 } else {
277 (from, to)
278 }
279 }
280
281 fn measure_new_cursor(&self, to: usize, editor_id: usize) -> TextCursor {
282 if self.mode == EditableMode::SingleLineMultipleEditors {
283 let row_char = self.line_to_char(editor_id);
284 let pos = self.char_to_utf16_cu(row_char) + to;
285 TextCursor::new(pos)
286 } else {
287 TextCursor::new(to)
288 }
289 }
290
291 fn set_selection(&mut self, selected: (usize, usize)) {
292 self.selected = Some(selected);
293 }
294
295 fn get_selected_text(&self) -> Option<String> {
296 let (start, end) = self.get_selection_range()?;
297
298 let start = self.utf16_cu_to_char(start);
299 let end = self.utf16_cu_to_char(end);
300
301 Some(self.rope().get_slice(start..end)?.to_string())
302 }
303
304 fn get_selection_range(&self) -> Option<(usize, usize)> {
305 let (start, end) = self.selected?;
306
307 let (start, end) = if start < end {
309 (start, end)
310 } else {
311 (end, start)
312 };
313
314 Some((start, end))
315 }
316
317 fn undo(&mut self) -> Option<usize> {
318 self.history.undo(&mut self.rope)
319 }
320
321 fn redo(&mut self) -> Option<usize> {
322 self.history.redo(&mut self.rope)
323 }
324
325 fn editor_history(&mut self) -> &mut EditorHistory {
326 &mut self.history
327 }
328
329 fn get_identation(&self) -> u8 {
330 self.identation
331 }
332}
333
334pub struct LinesIterator<'a> {
336 pub lines: Lines<'a>,
337}
338
339impl<'a> Iterator for LinesIterator<'a> {
340 type Item = Line<'a>;
341
342 fn next(&mut self) -> Option<Self::Item> {
343 let line = self.lines.next();
344
345 line.map(|line| Line {
346 text: line.into(),
347 utf16_len: line.len_utf16_cu(),
348 })
349 }
350}