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::Tab => {
121 if !self.is_grouping {
124 self.history.begin_group("Tab");
125 self.is_grouping = true;
126 }
127
128 let (line, col) = self.cursor;
129 for i in 0..4 {
131 let current_col = col + i;
132 let mut cmd = InsertCharCommand::new(
133 line,
134 current_col,
135 ' ',
136 (line, current_col),
137 );
138 cmd.execute(&mut self.buffer, &mut self.cursor);
139 self.history.push(Box::new(cmd));
140 }
141
142 self.reset_cursor_blink();
143 self.cache.clear();
144 Task::none()
145 }
146 Message::ArrowKey(direction, shift_pressed) => {
147 if self.is_grouping {
149 self.history.end_group();
150 self.is_grouping = false;
151 }
152
153 if *shift_pressed {
154 if self.selection_start.is_none() {
156 self.selection_start = Some(self.cursor);
157 }
158 self.move_cursor(*direction);
159 self.selection_end = Some(self.cursor);
160 } else {
161 self.clear_selection();
163 self.move_cursor(*direction);
164 }
165 self.reset_cursor_blink();
166 self.cache.clear();
167 self.scroll_to_cursor()
168 }
169 Message::MouseClick(point) => {
170 if self.is_grouping {
172 self.history.end_group();
173 self.is_grouping = false;
174 }
175
176 self.handle_mouse_click(*point);
177 self.reset_cursor_blink();
178 self.clear_selection();
180 self.is_dragging = true;
181 self.selection_start = Some(self.cursor);
182 Task::none()
183 }
184 Message::MouseDrag(point) => {
185 if self.is_dragging {
186 self.handle_mouse_drag(*point);
187 self.cache.clear();
188 }
189 Task::none()
190 }
191 Message::MouseRelease => {
192 self.is_dragging = false;
193 Task::none()
194 }
195 Message::Copy => self.copy_selection(),
196 Message::Paste(text) => {
197 if self.is_grouping {
199 self.history.end_group();
200 self.is_grouping = false;
201 }
202
203 if text.is_empty() {
205 iced::clipboard::read().and_then(|clipboard_text| {
207 Task::done(Message::Paste(clipboard_text))
208 })
209 } else {
210 self.paste_text(text);
212 self.cache.clear();
213 self.scroll_to_cursor()
214 }
215 }
216 Message::DeleteSelection => {
217 if self.is_grouping {
219 self.history.end_group();
220 self.is_grouping = false;
221 }
222
223 self.delete_selection();
225 self.reset_cursor_blink();
226 self.cache.clear();
227 self.scroll_to_cursor()
228 }
229 Message::Tick => {
230 if self.last_blink.elapsed() >= CURSOR_BLINK_INTERVAL {
232 self.cursor_visible = !self.cursor_visible;
233 self.last_blink = std::time::Instant::now();
234 self.cache.clear();
235 }
236 Task::none()
237 }
238 Message::PageUp => {
239 self.page_up();
240 self.reset_cursor_blink();
241 self.scroll_to_cursor()
242 }
243 Message::PageDown => {
244 self.page_down();
245 self.reset_cursor_blink();
246 self.scroll_to_cursor()
247 }
248 Message::Home(shift_pressed) => {
249 if *shift_pressed {
250 if self.selection_start.is_none() {
252 self.selection_start = Some(self.cursor);
253 }
254 self.cursor.1 = 0; self.selection_end = Some(self.cursor);
256 } else {
257 self.clear_selection();
259 self.cursor.1 = 0;
260 }
261 self.reset_cursor_blink();
262 self.cache.clear();
263 Task::none()
264 }
265 Message::End(shift_pressed) => {
266 let line = self.cursor.0;
267 let line_len = self.buffer.line_len(line);
268
269 if *shift_pressed {
270 if self.selection_start.is_none() {
272 self.selection_start = Some(self.cursor);
273 }
274 self.cursor.1 = line_len; self.selection_end = Some(self.cursor);
276 } else {
277 self.clear_selection();
279 self.cursor.1 = line_len;
280 }
281 self.reset_cursor_blink();
282 self.cache.clear();
283 Task::none()
284 }
285 Message::CtrlHome => {
286 self.clear_selection();
288 self.cursor = (0, 0);
289 self.reset_cursor_blink();
290 self.cache.clear();
291 self.scroll_to_cursor()
292 }
293 Message::CtrlEnd => {
294 self.clear_selection();
296 let last_line = self.buffer.line_count().saturating_sub(1);
297 let last_col = self.buffer.line_len(last_line);
298 self.cursor = (last_line, last_col);
299 self.reset_cursor_blink();
300 self.cache.clear();
301 self.scroll_to_cursor()
302 }
303 Message::Scrolled(viewport) => {
304 self.viewport_scroll = viewport.absolute_offset().y;
306 self.viewport_height = viewport.bounds().height;
307 Task::none()
308 }
309 Message::Undo => {
310 if self.is_grouping {
312 self.history.end_group();
313 self.is_grouping = false;
314 }
315
316 if self.history.undo(&mut self.buffer, &mut self.cursor) {
317 self.clear_selection();
318 self.reset_cursor_blink();
319 self.cache.clear();
320 self.scroll_to_cursor()
321 } else {
322 Task::none()
323 }
324 }
325 Message::Redo => {
326 if self.history.redo(&mut self.buffer, &mut self.cursor) {
327 self.clear_selection();
328 self.reset_cursor_blink();
329 self.cache.clear();
330 self.scroll_to_cursor()
331 } else {
332 Task::none()
333 }
334 }
335 }
336 }
337}
338
339#[cfg(test)]
340mod tests {
341 use super::*;
342 use crate::canvas_editor::ArrowDirection;
343
344 #[test]
345 fn test_new_canvas_editor() {
346 let editor = CodeEditor::new("line1\nline2", "py");
347 assert_eq!(editor.cursor, (0, 0));
348 }
349
350 #[test]
351 fn test_home_key() {
352 let mut editor = CodeEditor::new("hello world", "py");
353 editor.cursor = (0, 5); let _ = editor.update(&Message::Home(false));
355 assert_eq!(editor.cursor, (0, 0));
356 }
357
358 #[test]
359 fn test_end_key() {
360 let mut editor = CodeEditor::new("hello world", "py");
361 editor.cursor = (0, 0);
362 let _ = editor.update(&Message::End(false));
363 assert_eq!(editor.cursor, (0, 11)); }
365
366 #[test]
367 fn test_arrow_key_with_shift_creates_selection() {
368 let mut editor = CodeEditor::new("hello world", "py");
369 editor.cursor = (0, 0);
370
371 let _ = editor.update(&Message::ArrowKey(ArrowDirection::Right, true));
373 assert!(editor.selection_start.is_some());
374 assert!(editor.selection_end.is_some());
375 }
376
377 #[test]
378 fn test_arrow_key_without_shift_clears_selection() {
379 let mut editor = CodeEditor::new("hello world", "py");
380 editor.selection_start = Some((0, 0));
381 editor.selection_end = Some((0, 5));
382
383 let _ = editor.update(&Message::ArrowKey(ArrowDirection::Right, false));
385 assert_eq!(editor.selection_start, None);
386 assert_eq!(editor.selection_end, None);
387 }
388
389 #[test]
390 fn test_typing_with_selection() {
391 let mut editor = CodeEditor::new("hello world", "py");
392 editor.selection_start = Some((0, 0));
393 editor.selection_end = Some((0, 5));
394
395 let _ = editor.update(&Message::CharacterInput('X'));
396 assert_eq!(editor.buffer.line(0), "Xhello world");
399 }
400
401 #[test]
402 fn test_ctrl_home() {
403 let mut editor = CodeEditor::new("line1\nline2\nline3", "py");
404 editor.cursor = (2, 5); let _ = editor.update(&Message::CtrlHome);
406 assert_eq!(editor.cursor, (0, 0)); }
408
409 #[test]
410 fn test_ctrl_end() {
411 let mut editor = CodeEditor::new("line1\nline2\nline3", "py");
412 editor.cursor = (0, 0); let _ = editor.update(&Message::CtrlEnd);
414 assert_eq!(editor.cursor, (2, 5)); }
416
417 #[test]
418 fn test_ctrl_home_clears_selection() {
419 let mut editor = CodeEditor::new("line1\nline2\nline3", "py");
420 editor.cursor = (2, 5);
421 editor.selection_start = Some((0, 0));
422 editor.selection_end = Some((2, 5));
423
424 let _ = editor.update(&Message::CtrlHome);
425 assert_eq!(editor.cursor, (0, 0));
426 assert_eq!(editor.selection_start, None);
427 assert_eq!(editor.selection_end, None);
428 }
429
430 #[test]
431 fn test_ctrl_end_clears_selection() {
432 let mut editor = CodeEditor::new("line1\nline2\nline3", "py");
433 editor.cursor = (0, 0);
434 editor.selection_start = Some((0, 0));
435 editor.selection_end = Some((1, 3));
436
437 let _ = editor.update(&Message::CtrlEnd);
438 assert_eq!(editor.cursor, (2, 5));
439 assert_eq!(editor.selection_start, None);
440 assert_eq!(editor.selection_end, None);
441 }
442
443 #[test]
444 fn test_delete_selection_message() {
445 let mut editor = CodeEditor::new("hello world", "py");
446 editor.cursor = (0, 0);
447 editor.selection_start = Some((0, 0));
448 editor.selection_end = Some((0, 5));
449
450 let _ = editor.update(&Message::DeleteSelection);
451 assert_eq!(editor.buffer.line(0), " world");
452 assert_eq!(editor.cursor, (0, 0));
453 assert_eq!(editor.selection_start, None);
454 assert_eq!(editor.selection_end, None);
455 }
456
457 #[test]
458 fn test_delete_selection_multiline() {
459 let mut editor = CodeEditor::new("line1\nline2\nline3", "py");
460 editor.cursor = (0, 2);
461 editor.selection_start = Some((0, 2));
462 editor.selection_end = Some((2, 2));
463
464 let _ = editor.update(&Message::DeleteSelection);
465 assert_eq!(editor.buffer.line(0), "line3");
466 assert_eq!(editor.cursor, (0, 2));
467 assert_eq!(editor.selection_start, None);
468 }
469
470 #[test]
471 fn test_delete_selection_no_selection() {
472 let mut editor = CodeEditor::new("hello world", "py");
473 editor.cursor = (0, 5);
474
475 let _ = editor.update(&Message::DeleteSelection);
476 assert_eq!(editor.buffer.line(0), "hello world");
478 assert_eq!(editor.cursor, (0, 5));
479 }
480
481 #[test]
482 fn test_undo_char_insert() {
483 let mut editor = CodeEditor::new("hello", "py");
484 editor.cursor = (0, 5);
485
486 let _ = editor.update(&Message::CharacterInput('!'));
488 assert_eq!(editor.buffer.line(0), "hello!");
489 assert_eq!(editor.cursor, (0, 6));
490
491 editor.history.end_group();
493 let _ = editor.update(&Message::Undo);
494 assert_eq!(editor.buffer.line(0), "hello");
495 assert_eq!(editor.cursor, (0, 5));
496 }
497
498 #[test]
499 fn test_undo_redo_char_insert() {
500 let mut editor = CodeEditor::new("hello", "py");
501 editor.cursor = (0, 5);
502
503 let _ = editor.update(&Message::CharacterInput('!'));
505 editor.history.end_group();
506
507 let _ = editor.update(&Message::Undo);
509 assert_eq!(editor.buffer.line(0), "hello");
510
511 let _ = editor.update(&Message::Redo);
513 assert_eq!(editor.buffer.line(0), "hello!");
514 assert_eq!(editor.cursor, (0, 6));
515 }
516
517 #[test]
518 fn test_undo_backspace() {
519 let mut editor = CodeEditor::new("hello", "py");
520 editor.cursor = (0, 5);
521
522 let _ = editor.update(&Message::Backspace);
524 assert_eq!(editor.buffer.line(0), "hell");
525 assert_eq!(editor.cursor, (0, 4));
526
527 let _ = editor.update(&Message::Undo);
529 assert_eq!(editor.buffer.line(0), "hello");
530 assert_eq!(editor.cursor, (0, 5));
531 }
532
533 #[test]
534 fn test_undo_newline() {
535 let mut editor = CodeEditor::new("hello world", "py");
536 editor.cursor = (0, 5);
537
538 let _ = editor.update(&Message::Enter);
540 assert_eq!(editor.buffer.line(0), "hello");
541 assert_eq!(editor.buffer.line(1), " world");
542 assert_eq!(editor.cursor, (1, 0));
543
544 let _ = editor.update(&Message::Undo);
546 assert_eq!(editor.buffer.line(0), "hello world");
547 assert_eq!(editor.cursor, (0, 5));
548 }
549
550 #[test]
551 fn test_undo_grouped_typing() {
552 let mut editor = CodeEditor::new("hello", "py");
553 editor.cursor = (0, 5);
554
555 let _ = editor.update(&Message::CharacterInput(' '));
557 let _ = editor.update(&Message::CharacterInput('w'));
558 let _ = editor.update(&Message::CharacterInput('o'));
559 let _ = editor.update(&Message::CharacterInput('r'));
560 let _ = editor.update(&Message::CharacterInput('l'));
561 let _ = editor.update(&Message::CharacterInput('d'));
562
563 assert_eq!(editor.buffer.line(0), "hello world");
564
565 editor.history.end_group();
567
568 let _ = editor.update(&Message::Undo);
570 assert_eq!(editor.buffer.line(0), "hello");
571 assert_eq!(editor.cursor, (0, 5));
572 }
573
574 #[test]
575 fn test_navigation_ends_grouping() {
576 let mut editor = CodeEditor::new("hello", "py");
577 editor.cursor = (0, 5);
578
579 let _ = editor.update(&Message::CharacterInput('!'));
581 assert!(editor.is_grouping);
582
583 let _ = editor.update(&Message::ArrowKey(ArrowDirection::Left, false));
585 assert!(!editor.is_grouping);
586
587 let _ = editor.update(&Message::CharacterInput('?'));
589 assert!(editor.is_grouping);
590
591 editor.history.end_group();
592
593 let _ = editor.update(&Message::Undo);
595 assert_eq!(editor.buffer.line(0), "hello!");
596
597 let _ = editor.update(&Message::Undo);
598 assert_eq!(editor.buffer.line(0), "hello");
599 }
600
601 #[test]
602 fn test_multiple_undo_redo() {
603 let mut editor = CodeEditor::new("a", "py");
604 editor.cursor = (0, 1);
605
606 let _ = editor.update(&Message::CharacterInput('b'));
608 editor.history.end_group();
609
610 let _ = editor.update(&Message::CharacterInput('c'));
611 editor.history.end_group();
612
613 let _ = editor.update(&Message::CharacterInput('d'));
614 editor.history.end_group();
615
616 assert_eq!(editor.buffer.line(0), "abcd");
617
618 let _ = editor.update(&Message::Undo);
620 assert_eq!(editor.buffer.line(0), "abc");
621
622 let _ = editor.update(&Message::Undo);
623 assert_eq!(editor.buffer.line(0), "ab");
624
625 let _ = editor.update(&Message::Undo);
626 assert_eq!(editor.buffer.line(0), "a");
627
628 let _ = editor.update(&Message::Redo);
630 assert_eq!(editor.buffer.line(0), "ab");
631
632 let _ = editor.update(&Message::Redo);
633 assert_eq!(editor.buffer.line(0), "abc");
634
635 let _ = editor.update(&Message::Redo);
636 assert_eq!(editor.buffer.line(0), "abcd");
637 }
638
639 #[test]
640 fn test_delete_key_with_selection() {
641 let mut editor = CodeEditor::new("hello world", "py");
642 editor.selection_start = Some((0, 0));
643 editor.selection_end = Some((0, 5));
644 editor.cursor = (0, 5);
645
646 let _ = editor.update(&Message::Delete);
647
648 assert_eq!(editor.buffer.line(0), " world");
649 assert_eq!(editor.cursor, (0, 0));
650 assert_eq!(editor.selection_start, None);
651 assert_eq!(editor.selection_end, None);
652 }
653
654 #[test]
655 fn test_delete_key_without_selection() {
656 let mut editor = CodeEditor::new("hello", "py");
657 editor.cursor = (0, 0);
658
659 let _ = editor.update(&Message::Delete);
660
661 assert_eq!(editor.buffer.line(0), "ello");
663 assert_eq!(editor.cursor, (0, 0));
664 }
665
666 #[test]
667 fn test_backspace_with_selection() {
668 let mut editor = CodeEditor::new("hello world", "py");
669 editor.selection_start = Some((0, 6));
670 editor.selection_end = Some((0, 11));
671 editor.cursor = (0, 11);
672
673 let _ = editor.update(&Message::Backspace);
674
675 assert_eq!(editor.buffer.line(0), "hello ");
676 assert_eq!(editor.cursor, (0, 6));
677 assert_eq!(editor.selection_start, None);
678 assert_eq!(editor.selection_end, None);
679 }
680
681 #[test]
682 fn test_backspace_without_selection() {
683 let mut editor = CodeEditor::new("hello", "py");
684 editor.cursor = (0, 5);
685
686 let _ = editor.update(&Message::Backspace);
687
688 assert_eq!(editor.buffer.line(0), "hell");
690 assert_eq!(editor.cursor, (0, 4));
691 }
692
693 #[test]
694 fn test_delete_multiline_selection() {
695 let mut editor = CodeEditor::new("line1\nline2\nline3", "py");
696 editor.selection_start = Some((0, 2));
697 editor.selection_end = Some((2, 2));
698 editor.cursor = (2, 2);
699
700 let _ = editor.update(&Message::Delete);
701
702 assert_eq!(editor.buffer.line(0), "line3");
703 assert_eq!(editor.cursor, (0, 2));
704 assert_eq!(editor.selection_start, None);
705 }
706}