1use iced::Task;
4
5use super::command::{
6 Command, DeleteCharCommand, DeleteForwardCommand, InsertCharCommand,
7 InsertNewlineCommand,
8};
9use super::{CURSOR_BLINK_INTERVAL, CodeEditor, Message};
10
11impl CodeEditor {
12 pub fn update(&mut self, message: &Message) -> Task<Message> {
22 match message {
23 Message::CharacterInput(ch) => {
24 if !self.is_grouping {
26 self.history.begin_group("Typing");
27 self.is_grouping = true;
28 }
29
30 let (line, col) = self.cursor;
31 let mut cmd =
32 InsertCharCommand::new(line, col, *ch, self.cursor);
33 cmd.execute(&mut self.buffer, &mut self.cursor);
34 self.history.push(Box::new(cmd));
35
36 self.reset_cursor_blink();
37 self.cache.clear();
38 Task::none()
39 }
40 Message::Backspace => {
41 if self.is_grouping {
43 self.history.end_group();
44 self.is_grouping = false;
45 }
46
47 if self.selection_start.is_some()
49 && self.selection_end.is_some()
50 {
51 self.delete_selection();
52 self.reset_cursor_blink();
53 self.cache.clear();
54 return self.scroll_to_cursor();
55 }
56
57 let (line, col) = self.cursor;
59 let mut cmd = DeleteCharCommand::new(
60 &self.buffer,
61 line,
62 col,
63 self.cursor,
64 );
65 cmd.execute(&mut self.buffer, &mut self.cursor);
66 self.history.push(Box::new(cmd));
67
68 self.reset_cursor_blink();
69 self.cache.clear();
70 self.scroll_to_cursor()
71 }
72 Message::Delete => {
73 if self.is_grouping {
75 self.history.end_group();
76 self.is_grouping = false;
77 }
78
79 if self.selection_start.is_some()
81 && self.selection_end.is_some()
82 {
83 self.delete_selection();
84 self.reset_cursor_blink();
85 self.cache.clear();
86 return self.scroll_to_cursor();
87 }
88
89 let (line, col) = self.cursor;
91 let mut cmd = DeleteForwardCommand::new(
92 &self.buffer,
93 line,
94 col,
95 self.cursor,
96 );
97 cmd.execute(&mut self.buffer, &mut self.cursor);
98 self.history.push(Box::new(cmd));
99
100 self.reset_cursor_blink();
101 self.cache.clear();
102 Task::none()
103 }
104 Message::Enter => {
105 if self.is_grouping {
107 self.history.end_group();
108 self.is_grouping = false;
109 }
110
111 let (line, col) = self.cursor;
112 let mut cmd = InsertNewlineCommand::new(line, col, self.cursor);
113 cmd.execute(&mut self.buffer, &mut self.cursor);
114 self.history.push(Box::new(cmd));
115
116 self.reset_cursor_blink();
117 self.cache.clear();
118 self.scroll_to_cursor()
119 }
120 Message::ArrowKey(direction, shift_pressed) => {
121 if self.is_grouping {
123 self.history.end_group();
124 self.is_grouping = false;
125 }
126
127 if *shift_pressed {
128 if self.selection_start.is_none() {
130 self.selection_start = Some(self.cursor);
131 }
132 self.move_cursor(*direction);
133 self.selection_end = Some(self.cursor);
134 } else {
135 self.clear_selection();
137 self.move_cursor(*direction);
138 }
139 self.reset_cursor_blink();
140 self.cache.clear();
141 self.scroll_to_cursor()
142 }
143 Message::MouseClick(point) => {
144 if self.is_grouping {
146 self.history.end_group();
147 self.is_grouping = false;
148 }
149
150 self.handle_mouse_click(*point);
151 self.reset_cursor_blink();
152 self.clear_selection();
154 self.is_dragging = true;
155 self.selection_start = Some(self.cursor);
156 Task::none()
157 }
158 Message::MouseDrag(point) => {
159 if self.is_dragging {
160 self.handle_mouse_drag(*point);
161 self.cache.clear();
162 }
163 Task::none()
164 }
165 Message::MouseRelease => {
166 self.is_dragging = false;
167 Task::none()
168 }
169 Message::Copy => self.copy_selection(),
170 Message::Paste(text) => {
171 if self.is_grouping {
173 self.history.end_group();
174 self.is_grouping = false;
175 }
176
177 if text.is_empty() {
179 iced::clipboard::read().and_then(|clipboard_text| {
181 Task::done(Message::Paste(clipboard_text))
182 })
183 } else {
184 self.paste_text(text);
186 self.cache.clear();
187 self.scroll_to_cursor()
188 }
189 }
190 Message::DeleteSelection => {
191 if self.is_grouping {
193 self.history.end_group();
194 self.is_grouping = false;
195 }
196
197 self.delete_selection();
199 self.reset_cursor_blink();
200 self.cache.clear();
201 self.scroll_to_cursor()
202 }
203 Message::Tick => {
204 if self.last_blink.elapsed() >= CURSOR_BLINK_INTERVAL {
206 self.cursor_visible = !self.cursor_visible;
207 self.last_blink = std::time::Instant::now();
208 self.cache.clear();
209 }
210 Task::none()
211 }
212 Message::PageUp => {
213 self.page_up();
214 self.reset_cursor_blink();
215 self.scroll_to_cursor()
216 }
217 Message::PageDown => {
218 self.page_down();
219 self.reset_cursor_blink();
220 self.scroll_to_cursor()
221 }
222 Message::Home(shift_pressed) => {
223 if *shift_pressed {
224 if self.selection_start.is_none() {
226 self.selection_start = Some(self.cursor);
227 }
228 self.cursor.1 = 0; self.selection_end = Some(self.cursor);
230 } else {
231 self.clear_selection();
233 self.cursor.1 = 0;
234 }
235 self.reset_cursor_blink();
236 self.cache.clear();
237 Task::none()
238 }
239 Message::End(shift_pressed) => {
240 let line = self.cursor.0;
241 let line_len = self.buffer.line_len(line);
242
243 if *shift_pressed {
244 if self.selection_start.is_none() {
246 self.selection_start = Some(self.cursor);
247 }
248 self.cursor.1 = line_len; self.selection_end = Some(self.cursor);
250 } else {
251 self.clear_selection();
253 self.cursor.1 = line_len;
254 }
255 self.reset_cursor_blink();
256 self.cache.clear();
257 Task::none()
258 }
259 Message::CtrlHome => {
260 self.clear_selection();
262 self.cursor = (0, 0);
263 self.reset_cursor_blink();
264 self.cache.clear();
265 self.scroll_to_cursor()
266 }
267 Message::CtrlEnd => {
268 self.clear_selection();
270 let last_line = self.buffer.line_count().saturating_sub(1);
271 let last_col = self.buffer.line_len(last_line);
272 self.cursor = (last_line, last_col);
273 self.reset_cursor_blink();
274 self.cache.clear();
275 self.scroll_to_cursor()
276 }
277 Message::Scrolled(viewport) => {
278 self.viewport_scroll = viewport.absolute_offset().y;
280 self.viewport_height = viewport.bounds().height;
281 Task::none()
282 }
283 Message::Undo => {
284 if self.is_grouping {
286 self.history.end_group();
287 self.is_grouping = false;
288 }
289
290 if self.history.undo(&mut self.buffer, &mut self.cursor) {
291 self.clear_selection();
292 self.reset_cursor_blink();
293 self.cache.clear();
294 self.scroll_to_cursor()
295 } else {
296 Task::none()
297 }
298 }
299 Message::Redo => {
300 if self.history.redo(&mut self.buffer, &mut self.cursor) {
301 self.clear_selection();
302 self.reset_cursor_blink();
303 self.cache.clear();
304 self.scroll_to_cursor()
305 } else {
306 Task::none()
307 }
308 }
309 }
310 }
311}
312
313#[cfg(test)]
314mod tests {
315 use super::*;
316 use crate::canvas_editor::ArrowDirection;
317
318 #[test]
319 fn test_new_canvas_editor() {
320 let editor = CodeEditor::new("line1\nline2", "py");
321 assert_eq!(editor.cursor, (0, 0));
322 }
323
324 #[test]
325 fn test_home_key() {
326 let mut editor = CodeEditor::new("hello world", "py");
327 editor.cursor = (0, 5); let _ = editor.update(&Message::Home(false));
329 assert_eq!(editor.cursor, (0, 0));
330 }
331
332 #[test]
333 fn test_end_key() {
334 let mut editor = CodeEditor::new("hello world", "py");
335 editor.cursor = (0, 0);
336 let _ = editor.update(&Message::End(false));
337 assert_eq!(editor.cursor, (0, 11)); }
339
340 #[test]
341 fn test_arrow_key_with_shift_creates_selection() {
342 let mut editor = CodeEditor::new("hello world", "py");
343 editor.cursor = (0, 0);
344
345 let _ = editor.update(&Message::ArrowKey(ArrowDirection::Right, true));
347 assert!(editor.selection_start.is_some());
348 assert!(editor.selection_end.is_some());
349 }
350
351 #[test]
352 fn test_arrow_key_without_shift_clears_selection() {
353 let mut editor = CodeEditor::new("hello world", "py");
354 editor.selection_start = Some((0, 0));
355 editor.selection_end = Some((0, 5));
356
357 let _ = editor.update(&Message::ArrowKey(ArrowDirection::Right, false));
359 assert_eq!(editor.selection_start, None);
360 assert_eq!(editor.selection_end, None);
361 }
362
363 #[test]
364 fn test_typing_with_selection() {
365 let mut editor = CodeEditor::new("hello world", "py");
366 editor.selection_start = Some((0, 0));
367 editor.selection_end = Some((0, 5));
368
369 let _ = editor.update(&Message::CharacterInput('X'));
370 assert_eq!(editor.buffer.line(0), "Xhello world");
373 }
374
375 #[test]
376 fn test_ctrl_home() {
377 let mut editor = CodeEditor::new("line1\nline2\nline3", "py");
378 editor.cursor = (2, 5); let _ = editor.update(&Message::CtrlHome);
380 assert_eq!(editor.cursor, (0, 0)); }
382
383 #[test]
384 fn test_ctrl_end() {
385 let mut editor = CodeEditor::new("line1\nline2\nline3", "py");
386 editor.cursor = (0, 0); let _ = editor.update(&Message::CtrlEnd);
388 assert_eq!(editor.cursor, (2, 5)); }
390
391 #[test]
392 fn test_ctrl_home_clears_selection() {
393 let mut editor = CodeEditor::new("line1\nline2\nline3", "py");
394 editor.cursor = (2, 5);
395 editor.selection_start = Some((0, 0));
396 editor.selection_end = Some((2, 5));
397
398 let _ = editor.update(&Message::CtrlHome);
399 assert_eq!(editor.cursor, (0, 0));
400 assert_eq!(editor.selection_start, None);
401 assert_eq!(editor.selection_end, None);
402 }
403
404 #[test]
405 fn test_ctrl_end_clears_selection() {
406 let mut editor = CodeEditor::new("line1\nline2\nline3", "py");
407 editor.cursor = (0, 0);
408 editor.selection_start = Some((0, 0));
409 editor.selection_end = Some((1, 3));
410
411 let _ = editor.update(&Message::CtrlEnd);
412 assert_eq!(editor.cursor, (2, 5));
413 assert_eq!(editor.selection_start, None);
414 assert_eq!(editor.selection_end, None);
415 }
416
417 #[test]
418 fn test_delete_selection_message() {
419 let mut editor = CodeEditor::new("hello world", "py");
420 editor.cursor = (0, 0);
421 editor.selection_start = Some((0, 0));
422 editor.selection_end = Some((0, 5));
423
424 let _ = editor.update(&Message::DeleteSelection);
425 assert_eq!(editor.buffer.line(0), " world");
426 assert_eq!(editor.cursor, (0, 0));
427 assert_eq!(editor.selection_start, None);
428 assert_eq!(editor.selection_end, None);
429 }
430
431 #[test]
432 fn test_delete_selection_multiline() {
433 let mut editor = CodeEditor::new("line1\nline2\nline3", "py");
434 editor.cursor = (0, 2);
435 editor.selection_start = Some((0, 2));
436 editor.selection_end = Some((2, 2));
437
438 let _ = editor.update(&Message::DeleteSelection);
439 assert_eq!(editor.buffer.line(0), "line3");
440 assert_eq!(editor.cursor, (0, 2));
441 assert_eq!(editor.selection_start, None);
442 }
443
444 #[test]
445 fn test_delete_selection_no_selection() {
446 let mut editor = CodeEditor::new("hello world", "py");
447 editor.cursor = (0, 5);
448
449 let _ = editor.update(&Message::DeleteSelection);
450 assert_eq!(editor.buffer.line(0), "hello world");
452 assert_eq!(editor.cursor, (0, 5));
453 }
454
455 #[test]
456 fn test_undo_char_insert() {
457 let mut editor = CodeEditor::new("hello", "py");
458 editor.cursor = (0, 5);
459
460 let _ = editor.update(&Message::CharacterInput('!'));
462 assert_eq!(editor.buffer.line(0), "hello!");
463 assert_eq!(editor.cursor, (0, 6));
464
465 editor.history.end_group();
467 let _ = editor.update(&Message::Undo);
468 assert_eq!(editor.buffer.line(0), "hello");
469 assert_eq!(editor.cursor, (0, 5));
470 }
471
472 #[test]
473 fn test_undo_redo_char_insert() {
474 let mut editor = CodeEditor::new("hello", "py");
475 editor.cursor = (0, 5);
476
477 let _ = editor.update(&Message::CharacterInput('!'));
479 editor.history.end_group();
480
481 let _ = editor.update(&Message::Undo);
483 assert_eq!(editor.buffer.line(0), "hello");
484
485 let _ = editor.update(&Message::Redo);
487 assert_eq!(editor.buffer.line(0), "hello!");
488 assert_eq!(editor.cursor, (0, 6));
489 }
490
491 #[test]
492 fn test_undo_backspace() {
493 let mut editor = CodeEditor::new("hello", "py");
494 editor.cursor = (0, 5);
495
496 let _ = editor.update(&Message::Backspace);
498 assert_eq!(editor.buffer.line(0), "hell");
499 assert_eq!(editor.cursor, (0, 4));
500
501 let _ = editor.update(&Message::Undo);
503 assert_eq!(editor.buffer.line(0), "hello");
504 assert_eq!(editor.cursor, (0, 5));
505 }
506
507 #[test]
508 fn test_undo_newline() {
509 let mut editor = CodeEditor::new("hello world", "py");
510 editor.cursor = (0, 5);
511
512 let _ = editor.update(&Message::Enter);
514 assert_eq!(editor.buffer.line(0), "hello");
515 assert_eq!(editor.buffer.line(1), " world");
516 assert_eq!(editor.cursor, (1, 0));
517
518 let _ = editor.update(&Message::Undo);
520 assert_eq!(editor.buffer.line(0), "hello world");
521 assert_eq!(editor.cursor, (0, 5));
522 }
523
524 #[test]
525 fn test_undo_grouped_typing() {
526 let mut editor = CodeEditor::new("hello", "py");
527 editor.cursor = (0, 5);
528
529 let _ = editor.update(&Message::CharacterInput(' '));
531 let _ = editor.update(&Message::CharacterInput('w'));
532 let _ = editor.update(&Message::CharacterInput('o'));
533 let _ = editor.update(&Message::CharacterInput('r'));
534 let _ = editor.update(&Message::CharacterInput('l'));
535 let _ = editor.update(&Message::CharacterInput('d'));
536
537 assert_eq!(editor.buffer.line(0), "hello world");
538
539 editor.history.end_group();
541
542 let _ = editor.update(&Message::Undo);
544 assert_eq!(editor.buffer.line(0), "hello");
545 assert_eq!(editor.cursor, (0, 5));
546 }
547
548 #[test]
549 fn test_navigation_ends_grouping() {
550 let mut editor = CodeEditor::new("hello", "py");
551 editor.cursor = (0, 5);
552
553 let _ = editor.update(&Message::CharacterInput('!'));
555 assert!(editor.is_grouping);
556
557 let _ = editor.update(&Message::ArrowKey(ArrowDirection::Left, false));
559 assert!(!editor.is_grouping);
560
561 let _ = editor.update(&Message::CharacterInput('?'));
563 assert!(editor.is_grouping);
564
565 editor.history.end_group();
566
567 let _ = editor.update(&Message::Undo);
569 assert_eq!(editor.buffer.line(0), "hello!");
570
571 let _ = editor.update(&Message::Undo);
572 assert_eq!(editor.buffer.line(0), "hello");
573 }
574
575 #[test]
576 fn test_multiple_undo_redo() {
577 let mut editor = CodeEditor::new("a", "py");
578 editor.cursor = (0, 1);
579
580 let _ = editor.update(&Message::CharacterInput('b'));
582 editor.history.end_group();
583
584 let _ = editor.update(&Message::CharacterInput('c'));
585 editor.history.end_group();
586
587 let _ = editor.update(&Message::CharacterInput('d'));
588 editor.history.end_group();
589
590 assert_eq!(editor.buffer.line(0), "abcd");
591
592 let _ = editor.update(&Message::Undo);
594 assert_eq!(editor.buffer.line(0), "abc");
595
596 let _ = editor.update(&Message::Undo);
597 assert_eq!(editor.buffer.line(0), "ab");
598
599 let _ = editor.update(&Message::Undo);
600 assert_eq!(editor.buffer.line(0), "a");
601
602 let _ = editor.update(&Message::Redo);
604 assert_eq!(editor.buffer.line(0), "ab");
605
606 let _ = editor.update(&Message::Redo);
607 assert_eq!(editor.buffer.line(0), "abc");
608
609 let _ = editor.update(&Message::Redo);
610 assert_eq!(editor.buffer.line(0), "abcd");
611 }
612
613 #[test]
614 fn test_delete_key_with_selection() {
615 let mut editor = CodeEditor::new("hello world", "py");
616 editor.selection_start = Some((0, 0));
617 editor.selection_end = Some((0, 5));
618 editor.cursor = (0, 5);
619
620 let _ = editor.update(&Message::Delete);
621
622 assert_eq!(editor.buffer.line(0), " world");
623 assert_eq!(editor.cursor, (0, 0));
624 assert_eq!(editor.selection_start, None);
625 assert_eq!(editor.selection_end, None);
626 }
627
628 #[test]
629 fn test_delete_key_without_selection() {
630 let mut editor = CodeEditor::new("hello", "py");
631 editor.cursor = (0, 0);
632
633 let _ = editor.update(&Message::Delete);
634
635 assert_eq!(editor.buffer.line(0), "ello");
637 assert_eq!(editor.cursor, (0, 0));
638 }
639
640 #[test]
641 fn test_backspace_with_selection() {
642 let mut editor = CodeEditor::new("hello world", "py");
643 editor.selection_start = Some((0, 6));
644 editor.selection_end = Some((0, 11));
645 editor.cursor = (0, 11);
646
647 let _ = editor.update(&Message::Backspace);
648
649 assert_eq!(editor.buffer.line(0), "hello ");
650 assert_eq!(editor.cursor, (0, 6));
651 assert_eq!(editor.selection_start, None);
652 assert_eq!(editor.selection_end, None);
653 }
654
655 #[test]
656 fn test_backspace_without_selection() {
657 let mut editor = CodeEditor::new("hello", "py");
658 editor.cursor = (0, 5);
659
660 let _ = editor.update(&Message::Backspace);
661
662 assert_eq!(editor.buffer.line(0), "hell");
664 assert_eq!(editor.cursor, (0, 4));
665 }
666
667 #[test]
668 fn test_delete_multiline_selection() {
669 let mut editor = CodeEditor::new("line1\nline2\nline3", "py");
670 editor.selection_start = Some((0, 2));
671 editor.selection_end = Some((2, 2));
672 editor.cursor = (2, 2);
673
674 let _ = editor.update(&Message::Delete);
675
676 assert_eq!(editor.buffer.line(0), "line3");
677 assert_eq!(editor.cursor, (0, 2));
678 assert_eq!(editor.selection_start, None);
679 }
680}