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#[derive(Clone, Debug)]
25pub struct PreeditState {
26 pub start: usize,
28 pub len: usize,
30}
31
32pub struct RopeEditor {
34 pub(crate) rope: Rope,
35 pub(crate) selection: TextSelection,
36 pub(crate) indentation: u8,
37 pub(crate) history: EditorHistory,
38 pub(crate) preedit: Option<PreeditState>,
39}
40
41impl Display for RopeEditor {
42 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
43 f.write_str(&self.rope.to_string())
44 }
45}
46
47impl RopeEditor {
48 pub fn new(
50 text: String,
51 selection: TextSelection,
52 indentation: u8,
53 history: EditorHistory,
54 ) -> Self {
55 Self {
56 rope: Rope::from_str(&text),
57 selection,
58 indentation,
59 history,
60 preedit: None,
61 }
62 }
63
64 pub fn rope(&self) -> &Rope {
65 &self.rope
66 }
67
68 pub fn set_preedit(&mut self, text: &str) {
74 let preedit_start = if let Some(preedit) = self.preedit.take() {
76 let start_char = self.rope.utf16_cu_to_char(preedit.start);
77 let end_char = self.rope.utf16_cu_to_char(preedit.start + preedit.len);
78 self.rope.remove(start_char..end_char);
79 preedit.start
80 } else {
81 self.cursor_pos()
82 };
83
84 let start_char = self.rope.utf16_cu_to_char(preedit_start);
86 let len_before = self.rope.len_utf16_cu();
87 self.rope.insert(start_char, text);
88 let len_after = self.rope.len_utf16_cu();
89 let preedit_len = len_after - len_before;
90
91 self.preedit = Some(PreeditState {
92 start: preedit_start,
93 len: preedit_len,
94 });
95 self.selection = TextSelection::Cursor(preedit_start + preedit_len);
96 }
97
98 pub fn clear_preedit(&mut self) {
100 if let Some(preedit) = self.preedit.take() {
101 let start_char = self.rope.utf16_cu_to_char(preedit.start);
102 let end_char = self.rope.utf16_cu_to_char(preedit.start + preedit.len);
103 self.rope.remove(start_char..end_char);
104 self.selection = TextSelection::Cursor(preedit.start);
105 }
106 }
107
108 pub fn has_preedit(&self) -> bool {
110 self.preedit.is_some()
111 }
112
113 pub fn committed_text(&self) -> String {
118 if let Some(preedit) = &self.preedit {
119 let start_char = self.rope.utf16_cu_to_char(preedit.start);
120 let end_char = self.rope.utf16_cu_to_char(preedit.start + preedit.len);
121 let before = self.rope.slice(..start_char);
122 let after = self.rope.slice(end_char..);
123 format!("{before}{after}")
124 } else {
125 self.rope.to_string()
126 }
127 }
128
129 pub fn preedit_text_segments(&self) -> (String, String, String) {
134 if let Some(preedit) = &self.preedit {
135 let start_char = self.rope.utf16_cu_to_char(preedit.start);
136 let end_char = self.rope.utf16_cu_to_char(preedit.start + preedit.len);
137 let before = self.rope.slice(..start_char).to_string();
138 let preedit_text = self.rope.slice(start_char..end_char).to_string();
139 let after = self.rope.slice(end_char..).to_string();
140 (before, preedit_text, after)
141 } else {
142 (self.rope.to_string(), String::new(), String::new())
143 }
144 }
145}
146
147impl TextEditor for RopeEditor {
148 type LinesIterator<'a> = LinesIterator<'a>;
149
150 fn lines(&self) -> Self::LinesIterator<'_> {
151 let lines = self.rope.lines();
152 LinesIterator { lines }
153 }
154
155 fn insert_char(&mut self, ch: char, idx: usize) -> usize {
156 let idx_utf8 = self.utf16_cu_to_char(idx);
157 let selection = self.selection.clone();
158
159 let len_before_insert = self.rope.len_utf16_cu();
160 self.rope.insert_char(idx_utf8, ch);
161 let len_after_insert = self.rope.len_utf16_cu();
162
163 let inserted_text_len = len_after_insert - len_before_insert;
164
165 self.history.push_change(HistoryChange::InsertChar {
166 idx,
167 ch,
168 len: inserted_text_len,
169 selection,
170 });
171
172 inserted_text_len
173 }
174
175 fn insert(&mut self, text: &str, idx: usize) -> usize {
176 let idx_utf8 = self.utf16_cu_to_char(idx);
177 let selection = self.selection.clone();
178
179 let len_before_insert = self.rope.len_utf16_cu();
180 self.rope.insert(idx_utf8, text);
181 let len_after_insert = self.rope.len_utf16_cu();
182
183 let inserted_text_len = len_after_insert - len_before_insert;
184
185 self.history.push_change(HistoryChange::InsertText {
186 idx,
187 text: text.to_owned(),
188 len: inserted_text_len,
189 selection,
190 });
191
192 inserted_text_len
193 }
194
195 fn remove(&mut self, range_utf16: Range<usize>) -> usize {
196 let range =
197 self.utf16_cu_to_char(range_utf16.start)..self.utf16_cu_to_char(range_utf16.end);
198 let text = self.rope.slice(range.clone()).to_string();
199 let selection = self.selection.clone();
200
201 let len_before_remove = self.rope.len_utf16_cu();
202 self.rope.remove(range);
203 let len_after_remove = self.rope.len_utf16_cu();
204
205 let removed_text_len = len_before_remove - len_after_remove;
206
207 self.history.push_change(HistoryChange::Remove {
208 idx: range_utf16.end - removed_text_len,
209 text,
210 len: removed_text_len,
211 selection,
212 });
213
214 removed_text_len
215 }
216
217 fn char_to_line(&self, char_idx: usize) -> usize {
218 self.rope.char_to_line(char_idx)
219 }
220
221 fn line_to_char(&self, line_idx: usize) -> usize {
222 self.rope.line_to_char(line_idx)
223 }
224
225 fn utf16_cu_to_char(&self, utf16_cu_idx: usize) -> usize {
226 self.rope.utf16_cu_to_char(utf16_cu_idx)
227 }
228
229 fn char_to_utf16_cu(&self, idx: usize) -> usize {
230 self.rope.char_to_utf16_cu(idx)
231 }
232
233 fn line(&self, line_idx: usize) -> Option<Line<'_>> {
234 let line = self.rope.get_line(line_idx);
235
236 line.map(|line| Line {
237 text: line.into(),
238 utf16_len: line.len_utf16_cu(),
239 })
240 }
241
242 fn len_lines(&self) -> usize {
243 self.rope.len_lines()
244 }
245
246 fn len_chars(&self) -> usize {
247 self.rope.len_chars()
248 }
249
250 fn len_utf16_cu(&self) -> usize {
251 self.rope.len_utf16_cu()
252 }
253
254 fn selection(&self) -> &TextSelection {
255 &self.selection
256 }
257
258 fn selection_mut(&mut self) -> &mut TextSelection {
259 &mut self.selection
260 }
261
262 fn has_any_selection(&self) -> bool {
263 self.selection.is_range()
264 }
265
266 fn get_selection(&self) -> Option<(usize, usize)> {
267 match self.selection {
268 TextSelection::Cursor(_) => None,
269 TextSelection::Range { from, to } => Some((from, to)),
270 }
271 }
272
273 fn set(&mut self, text: &str) {
274 self.rope.remove(0..);
275 self.rope.insert(0, text);
276 if self.cursor_pos() > text.len() {
277 self.move_cursor_to(text.len());
278 }
279 }
280
281 fn clear_selection(&mut self) {
282 let end = self.selection().end();
283 self.selection_mut().set_as_cursor();
284 self.selection_mut().move_to(end);
285 }
286
287 fn set_selection(&mut self, (from, to): (usize, usize)) {
288 self.selection = TextSelection::Range { from, to };
289 }
290
291 fn get_selected_text(&self) -> Option<String> {
292 let (start, end) = self.get_selection_range()?;
293
294 let start = self.utf16_cu_to_char(start);
295 let end = self.utf16_cu_to_char(end);
296
297 Some(self.rope().get_slice(start..end)?.to_string())
298 }
299
300 fn get_selection_range(&self) -> Option<(usize, usize)> {
301 let (start, end) = match self.selection {
302 TextSelection::Cursor(_) => return None,
303 TextSelection::Range { from, to } => (from, to),
304 };
305
306 let (start, end) = if start < end {
308 (start, end)
309 } else {
310 (end, start)
311 };
312
313 Some((start, end))
314 }
315
316 fn undo(&mut self) -> Option<TextSelection> {
317 self.history.undo(&mut self.rope)
318 }
319
320 fn redo(&mut self) -> Option<TextSelection> {
321 self.history.redo(&mut self.rope)
322 }
323
324 fn editor_history(&mut self) -> &mut EditorHistory {
325 &mut self.history
326 }
327
328 fn get_indentation(&self) -> u8 {
329 self.indentation
330 }
331}
332
333pub struct LinesIterator<'a> {
335 pub lines: Lines<'a>,
336}
337
338impl<'a> Iterator for LinesIterator<'a> {
339 type Item = Line<'a>;
340
341 fn next(&mut self) -> Option<Self::Item> {
342 let line = self.lines.next();
343
344 line.map(|line| Line {
345 text: line.into(),
346 utf16_len: line.len_utf16_cu(),
347 })
348 }
349}
350
351#[cfg(test)]
352mod test {
353 use std::time::Duration;
354
355 use super::RopeEditor;
356 use crate::{
357 EditorHistory,
358 TextSelection,
359 text_editor::TextEditor,
360 };
361
362 fn editor(text: &str) -> RopeEditor {
363 RopeEditor::new(
364 text.to_string(),
365 TextSelection::new_cursor(0),
366 4,
367 EditorHistory::new(Duration::ZERO),
368 )
369 }
370
371 #[test]
372 fn preedit_lifecycle() {
373 let mut ed = editor("Hello World");
374 ed.move_cursor_to(5);
376
377 assert!(!ed.has_preedit());
379 assert_eq!(ed.committed_text(), "Hello World");
380
381 ed.set_preedit("你好");
383 assert!(ed.has_preedit());
384 assert_eq!(ed.rope().to_string(), "Hello你好 World");
385 assert_eq!(ed.committed_text(), "Hello World");
386 assert_eq!(ed.cursor_pos(), 5 + "你好".encode_utf16().count());
387
388 ed.set_preedit("世界abc");
390 assert!(ed.has_preedit());
391 assert_eq!(ed.rope().to_string(), "Hello世界abc World");
392 assert_eq!(ed.committed_text(), "Hello World");
393 assert_eq!(ed.cursor_pos(), 5 + "世界abc".encode_utf16().count());
394
395 ed.clear_preedit();
397 assert!(!ed.has_preedit());
398 assert_eq!(ed.rope().to_string(), "Hello World");
399 assert_eq!(ed.committed_text(), "Hello World");
400 assert_eq!(ed.cursor_pos(), 5);
401 }
402
403 #[test]
404 fn preedit_skips_undo_history_and_clear_restores() {
405 let mut ed = editor("Hello");
406 ed.move_cursor_to(5);
407 assert!(!ed.history.can_undo());
408
409 ed.set_preedit("XY");
411 assert!(!ed.history.can_undo());
412 assert_eq!(ed.rope().to_string(), "HelloXY");
413
414 ed.set_preedit("Z");
416 assert!(!ed.history.can_undo());
417 assert_eq!(ed.rope().to_string(), "HelloZ");
418
419 ed.clear_preedit();
421 assert!(!ed.has_preedit());
422 assert!(!ed.history.can_undo());
423 assert_eq!(ed.rope().to_string(), "Hello");
424 assert_eq!(ed.cursor_pos(), 5);
425
426 ed.clear_preedit();
428 assert_eq!(ed.rope().to_string(), "Hello");
429 assert_eq!(ed.cursor_pos(), 5);
430 }
431}