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 let new_height = viewport.bounds().height;
307 if (self.viewport_height - new_height).abs() > 1.0 {
310 self.cache.clear();
311 }
312 self.viewport_height = new_height;
313 Task::none()
314 }
315 Message::Undo => {
316 if self.is_grouping {
318 self.history.end_group();
319 self.is_grouping = false;
320 }
321
322 if self.history.undo(&mut self.buffer, &mut self.cursor) {
323 self.clear_selection();
324 self.reset_cursor_blink();
325 self.cache.clear();
326 self.scroll_to_cursor()
327 } else {
328 Task::none()
329 }
330 }
331 Message::Redo => {
332 if self.history.redo(&mut self.buffer, &mut self.cursor) {
333 self.clear_selection();
334 self.reset_cursor_blink();
335 self.cache.clear();
336 self.scroll_to_cursor()
337 } else {
338 Task::none()
339 }
340 }
341 }
342 }
343}
344
345#[cfg(test)]
346mod tests {
347 use super::*;
348 use crate::canvas_editor::ArrowDirection;
349
350 #[test]
351 fn test_new_canvas_editor() {
352 let editor = CodeEditor::new("line1\nline2", "py");
353 assert_eq!(editor.cursor, (0, 0));
354 }
355
356 #[test]
357 fn test_home_key() {
358 let mut editor = CodeEditor::new("hello world", "py");
359 editor.cursor = (0, 5); let _ = editor.update(&Message::Home(false));
361 assert_eq!(editor.cursor, (0, 0));
362 }
363
364 #[test]
365 fn test_end_key() {
366 let mut editor = CodeEditor::new("hello world", "py");
367 editor.cursor = (0, 0);
368 let _ = editor.update(&Message::End(false));
369 assert_eq!(editor.cursor, (0, 11)); }
371
372 #[test]
373 fn test_arrow_key_with_shift_creates_selection() {
374 let mut editor = CodeEditor::new("hello world", "py");
375 editor.cursor = (0, 0);
376
377 let _ = editor.update(&Message::ArrowKey(ArrowDirection::Right, true));
379 assert!(editor.selection_start.is_some());
380 assert!(editor.selection_end.is_some());
381 }
382
383 #[test]
384 fn test_arrow_key_without_shift_clears_selection() {
385 let mut editor = CodeEditor::new("hello world", "py");
386 editor.selection_start = Some((0, 0));
387 editor.selection_end = Some((0, 5));
388
389 let _ = editor.update(&Message::ArrowKey(ArrowDirection::Right, false));
391 assert_eq!(editor.selection_start, None);
392 assert_eq!(editor.selection_end, None);
393 }
394
395 #[test]
396 fn test_typing_with_selection() {
397 let mut editor = CodeEditor::new("hello world", "py");
398 editor.selection_start = Some((0, 0));
399 editor.selection_end = Some((0, 5));
400
401 let _ = editor.update(&Message::CharacterInput('X'));
402 assert_eq!(editor.buffer.line(0), "Xhello world");
405 }
406
407 #[test]
408 fn test_ctrl_home() {
409 let mut editor = CodeEditor::new("line1\nline2\nline3", "py");
410 editor.cursor = (2, 5); let _ = editor.update(&Message::CtrlHome);
412 assert_eq!(editor.cursor, (0, 0)); }
414
415 #[test]
416 fn test_ctrl_end() {
417 let mut editor = CodeEditor::new("line1\nline2\nline3", "py");
418 editor.cursor = (0, 0); let _ = editor.update(&Message::CtrlEnd);
420 assert_eq!(editor.cursor, (2, 5)); }
422
423 #[test]
424 fn test_ctrl_home_clears_selection() {
425 let mut editor = CodeEditor::new("line1\nline2\nline3", "py");
426 editor.cursor = (2, 5);
427 editor.selection_start = Some((0, 0));
428 editor.selection_end = Some((2, 5));
429
430 let _ = editor.update(&Message::CtrlHome);
431 assert_eq!(editor.cursor, (0, 0));
432 assert_eq!(editor.selection_start, None);
433 assert_eq!(editor.selection_end, None);
434 }
435
436 #[test]
437 fn test_ctrl_end_clears_selection() {
438 let mut editor = CodeEditor::new("line1\nline2\nline3", "py");
439 editor.cursor = (0, 0);
440 editor.selection_start = Some((0, 0));
441 editor.selection_end = Some((1, 3));
442
443 let _ = editor.update(&Message::CtrlEnd);
444 assert_eq!(editor.cursor, (2, 5));
445 assert_eq!(editor.selection_start, None);
446 assert_eq!(editor.selection_end, None);
447 }
448
449 #[test]
450 fn test_delete_selection_message() {
451 let mut editor = CodeEditor::new("hello world", "py");
452 editor.cursor = (0, 0);
453 editor.selection_start = Some((0, 0));
454 editor.selection_end = Some((0, 5));
455
456 let _ = editor.update(&Message::DeleteSelection);
457 assert_eq!(editor.buffer.line(0), " world");
458 assert_eq!(editor.cursor, (0, 0));
459 assert_eq!(editor.selection_start, None);
460 assert_eq!(editor.selection_end, None);
461 }
462
463 #[test]
464 fn test_delete_selection_multiline() {
465 let mut editor = CodeEditor::new("line1\nline2\nline3", "py");
466 editor.cursor = (0, 2);
467 editor.selection_start = Some((0, 2));
468 editor.selection_end = Some((2, 2));
469
470 let _ = editor.update(&Message::DeleteSelection);
471 assert_eq!(editor.buffer.line(0), "line3");
472 assert_eq!(editor.cursor, (0, 2));
473 assert_eq!(editor.selection_start, None);
474 }
475
476 #[test]
477 fn test_delete_selection_no_selection() {
478 let mut editor = CodeEditor::new("hello world", "py");
479 editor.cursor = (0, 5);
480
481 let _ = editor.update(&Message::DeleteSelection);
482 assert_eq!(editor.buffer.line(0), "hello world");
484 assert_eq!(editor.cursor, (0, 5));
485 }
486
487 #[test]
488 fn test_undo_char_insert() {
489 let mut editor = CodeEditor::new("hello", "py");
490 editor.cursor = (0, 5);
491
492 let _ = editor.update(&Message::CharacterInput('!'));
494 assert_eq!(editor.buffer.line(0), "hello!");
495 assert_eq!(editor.cursor, (0, 6));
496
497 editor.history.end_group();
499 let _ = editor.update(&Message::Undo);
500 assert_eq!(editor.buffer.line(0), "hello");
501 assert_eq!(editor.cursor, (0, 5));
502 }
503
504 #[test]
505 fn test_undo_redo_char_insert() {
506 let mut editor = CodeEditor::new("hello", "py");
507 editor.cursor = (0, 5);
508
509 let _ = editor.update(&Message::CharacterInput('!'));
511 editor.history.end_group();
512
513 let _ = editor.update(&Message::Undo);
515 assert_eq!(editor.buffer.line(0), "hello");
516
517 let _ = editor.update(&Message::Redo);
519 assert_eq!(editor.buffer.line(0), "hello!");
520 assert_eq!(editor.cursor, (0, 6));
521 }
522
523 #[test]
524 fn test_undo_backspace() {
525 let mut editor = CodeEditor::new("hello", "py");
526 editor.cursor = (0, 5);
527
528 let _ = editor.update(&Message::Backspace);
530 assert_eq!(editor.buffer.line(0), "hell");
531 assert_eq!(editor.cursor, (0, 4));
532
533 let _ = editor.update(&Message::Undo);
535 assert_eq!(editor.buffer.line(0), "hello");
536 assert_eq!(editor.cursor, (0, 5));
537 }
538
539 #[test]
540 fn test_undo_newline() {
541 let mut editor = CodeEditor::new("hello world", "py");
542 editor.cursor = (0, 5);
543
544 let _ = editor.update(&Message::Enter);
546 assert_eq!(editor.buffer.line(0), "hello");
547 assert_eq!(editor.buffer.line(1), " world");
548 assert_eq!(editor.cursor, (1, 0));
549
550 let _ = editor.update(&Message::Undo);
552 assert_eq!(editor.buffer.line(0), "hello world");
553 assert_eq!(editor.cursor, (0, 5));
554 }
555
556 #[test]
557 fn test_undo_grouped_typing() {
558 let mut editor = CodeEditor::new("hello", "py");
559 editor.cursor = (0, 5);
560
561 let _ = editor.update(&Message::CharacterInput(' '));
563 let _ = editor.update(&Message::CharacterInput('w'));
564 let _ = editor.update(&Message::CharacterInput('o'));
565 let _ = editor.update(&Message::CharacterInput('r'));
566 let _ = editor.update(&Message::CharacterInput('l'));
567 let _ = editor.update(&Message::CharacterInput('d'));
568
569 assert_eq!(editor.buffer.line(0), "hello world");
570
571 editor.history.end_group();
573
574 let _ = editor.update(&Message::Undo);
576 assert_eq!(editor.buffer.line(0), "hello");
577 assert_eq!(editor.cursor, (0, 5));
578 }
579
580 #[test]
581 fn test_navigation_ends_grouping() {
582 let mut editor = CodeEditor::new("hello", "py");
583 editor.cursor = (0, 5);
584
585 let _ = editor.update(&Message::CharacterInput('!'));
587 assert!(editor.is_grouping);
588
589 let _ = editor.update(&Message::ArrowKey(ArrowDirection::Left, false));
591 assert!(!editor.is_grouping);
592
593 let _ = editor.update(&Message::CharacterInput('?'));
595 assert!(editor.is_grouping);
596
597 editor.history.end_group();
598
599 let _ = editor.update(&Message::Undo);
601 assert_eq!(editor.buffer.line(0), "hello!");
602
603 let _ = editor.update(&Message::Undo);
604 assert_eq!(editor.buffer.line(0), "hello");
605 }
606
607 #[test]
608 fn test_multiple_undo_redo() {
609 let mut editor = CodeEditor::new("a", "py");
610 editor.cursor = (0, 1);
611
612 let _ = editor.update(&Message::CharacterInput('b'));
614 editor.history.end_group();
615
616 let _ = editor.update(&Message::CharacterInput('c'));
617 editor.history.end_group();
618
619 let _ = editor.update(&Message::CharacterInput('d'));
620 editor.history.end_group();
621
622 assert_eq!(editor.buffer.line(0), "abcd");
623
624 let _ = editor.update(&Message::Undo);
626 assert_eq!(editor.buffer.line(0), "abc");
627
628 let _ = editor.update(&Message::Undo);
629 assert_eq!(editor.buffer.line(0), "ab");
630
631 let _ = editor.update(&Message::Undo);
632 assert_eq!(editor.buffer.line(0), "a");
633
634 let _ = editor.update(&Message::Redo);
636 assert_eq!(editor.buffer.line(0), "ab");
637
638 let _ = editor.update(&Message::Redo);
639 assert_eq!(editor.buffer.line(0), "abc");
640
641 let _ = editor.update(&Message::Redo);
642 assert_eq!(editor.buffer.line(0), "abcd");
643 }
644
645 #[test]
646 fn test_delete_key_with_selection() {
647 let mut editor = CodeEditor::new("hello world", "py");
648 editor.selection_start = Some((0, 0));
649 editor.selection_end = Some((0, 5));
650 editor.cursor = (0, 5);
651
652 let _ = editor.update(&Message::Delete);
653
654 assert_eq!(editor.buffer.line(0), " world");
655 assert_eq!(editor.cursor, (0, 0));
656 assert_eq!(editor.selection_start, None);
657 assert_eq!(editor.selection_end, None);
658 }
659
660 #[test]
661 fn test_delete_key_without_selection() {
662 let mut editor = CodeEditor::new("hello", "py");
663 editor.cursor = (0, 0);
664
665 let _ = editor.update(&Message::Delete);
666
667 assert_eq!(editor.buffer.line(0), "ello");
669 assert_eq!(editor.cursor, (0, 0));
670 }
671
672 #[test]
673 fn test_backspace_with_selection() {
674 let mut editor = CodeEditor::new("hello world", "py");
675 editor.selection_start = Some((0, 6));
676 editor.selection_end = Some((0, 11));
677 editor.cursor = (0, 11);
678
679 let _ = editor.update(&Message::Backspace);
680
681 assert_eq!(editor.buffer.line(0), "hello ");
682 assert_eq!(editor.cursor, (0, 6));
683 assert_eq!(editor.selection_start, None);
684 assert_eq!(editor.selection_end, None);
685 }
686
687 #[test]
688 fn test_backspace_without_selection() {
689 let mut editor = CodeEditor::new("hello", "py");
690 editor.cursor = (0, 5);
691
692 let _ = editor.update(&Message::Backspace);
693
694 assert_eq!(editor.buffer.line(0), "hell");
696 assert_eq!(editor.cursor, (0, 4));
697 }
698
699 #[test]
700 fn test_delete_multiline_selection() {
701 let mut editor = CodeEditor::new("line1\nline2\nline3", "py");
702 editor.selection_start = Some((0, 2));
703 editor.selection_end = Some((2, 2));
704 editor.cursor = (2, 2);
705
706 let _ = editor.update(&Message::Delete);
707
708 assert_eq!(editor.buffer.line(0), "line3");
709 assert_eq!(editor.cursor, (0, 2));
710 assert_eq!(editor.selection_start, None);
711 }
712}