1use iced::Task;
4use iced::widget::operation::{focus, select_all};
5
6use super::command::{
7 Command, CompositeCommand, DeleteCharCommand, DeleteForwardCommand,
8 InsertCharCommand, InsertNewlineCommand, ReplaceTextCommand,
9};
10use super::{CURSOR_BLINK_INTERVAL, CodeEditor, Message};
11
12impl CodeEditor {
13 pub fn update(&mut self, message: &Message) -> Task<Message> {
23 match message {
24 Message::CharacterInput(ch) => {
25 if !self.is_grouping {
27 self.history.begin_group("Typing");
28 self.is_grouping = true;
29 }
30
31 let (line, col) = self.cursor;
32 let mut cmd =
33 InsertCharCommand::new(line, col, *ch, self.cursor);
34 cmd.execute(&mut self.buffer, &mut self.cursor);
35 self.history.push(Box::new(cmd));
36
37 self.reset_cursor_blink();
38 self.refresh_search_matches_if_needed();
39 self.cache.clear();
40 Task::none()
41 }
42 Message::Backspace => {
43 if self.is_grouping {
45 self.history.end_group();
46 self.is_grouping = false;
47 }
48
49 if self.selection_start.is_some()
51 && self.selection_end.is_some()
52 {
53 self.delete_selection();
54 self.reset_cursor_blink();
55 self.refresh_search_matches_if_needed();
56 self.cache.clear();
57 return self.scroll_to_cursor();
58 }
59
60 let (line, col) = self.cursor;
62 let mut cmd = DeleteCharCommand::new(
63 &self.buffer,
64 line,
65 col,
66 self.cursor,
67 );
68 cmd.execute(&mut self.buffer, &mut self.cursor);
69 self.history.push(Box::new(cmd));
70
71 self.reset_cursor_blink();
72 self.refresh_search_matches_if_needed();
73 self.cache.clear();
74 self.scroll_to_cursor()
75 }
76 Message::Delete => {
77 if self.is_grouping {
79 self.history.end_group();
80 self.is_grouping = false;
81 }
82
83 if self.selection_start.is_some()
85 && self.selection_end.is_some()
86 {
87 self.delete_selection();
88 self.reset_cursor_blink();
89 self.refresh_search_matches_if_needed();
90 self.cache.clear();
91 return self.scroll_to_cursor();
92 }
93
94 let (line, col) = self.cursor;
96 let mut cmd = DeleteForwardCommand::new(
97 &self.buffer,
98 line,
99 col,
100 self.cursor,
101 );
102 cmd.execute(&mut self.buffer, &mut self.cursor);
103 self.history.push(Box::new(cmd));
104
105 self.reset_cursor_blink();
106 self.refresh_search_matches_if_needed();
107 self.cache.clear();
108 Task::none()
109 }
110 Message::Enter => {
111 if self.is_grouping {
113 self.history.end_group();
114 self.is_grouping = false;
115 }
116
117 let (line, col) = self.cursor;
118 let mut cmd = InsertNewlineCommand::new(line, col, self.cursor);
119 cmd.execute(&mut self.buffer, &mut self.cursor);
120 self.history.push(Box::new(cmd));
121
122 self.reset_cursor_blink();
123 self.refresh_search_matches_if_needed();
124 self.cache.clear();
125 self.scroll_to_cursor()
126 }
127 Message::Tab => {
128 if !self.is_grouping {
131 self.history.begin_group("Tab");
132 self.is_grouping = true;
133 }
134
135 let (line, col) = self.cursor;
136 for i in 0..4 {
138 let current_col = col + i;
139 let mut cmd = InsertCharCommand::new(
140 line,
141 current_col,
142 ' ',
143 (line, current_col),
144 );
145 cmd.execute(&mut self.buffer, &mut self.cursor);
146 self.history.push(Box::new(cmd));
147 }
148
149 self.reset_cursor_blink();
150 self.cache.clear();
151 Task::none()
152 }
153 Message::ArrowKey(direction, shift_pressed) => {
154 if self.is_grouping {
156 self.history.end_group();
157 self.is_grouping = false;
158 }
159
160 if *shift_pressed {
161 if self.selection_start.is_none() {
163 self.selection_start = Some(self.cursor);
164 }
165 self.move_cursor(*direction);
166 self.selection_end = Some(self.cursor);
167 } else {
168 self.clear_selection();
170 self.move_cursor(*direction);
171 }
172 self.reset_cursor_blink();
173 self.cache.clear();
174 self.scroll_to_cursor()
175 }
176 Message::MouseClick(point) => {
177 super::FOCUSED_EDITOR_ID.store(
179 self.editor_id,
180 std::sync::atomic::Ordering::Relaxed,
181 );
182
183 if self.is_grouping {
185 self.history.end_group();
186 self.is_grouping = false;
187 }
188
189 self.handle_mouse_click(*point);
190 self.reset_cursor_blink();
191 self.clear_selection();
193 self.is_dragging = true;
194 self.selection_start = Some(self.cursor);
195
196 self.has_canvas_focus = true;
198 self.show_cursor = true;
199
200 Task::none()
201 }
202 Message::MouseDrag(point) => {
203 if self.is_dragging {
204 self.handle_mouse_drag(*point);
205 self.cache.clear();
206 }
207 Task::none()
208 }
209 Message::MouseRelease => {
210 self.is_dragging = false;
211 Task::none()
212 }
213 Message::Copy => self.copy_selection(),
214 Message::Paste(text) => {
215 if self.is_grouping {
217 self.history.end_group();
218 self.is_grouping = false;
219 }
220
221 if text.is_empty() {
223 iced::clipboard::read().and_then(|clipboard_text| {
225 Task::done(Message::Paste(clipboard_text))
226 })
227 } else {
228 self.paste_text(text);
230 self.refresh_search_matches_if_needed();
231 self.cache.clear();
232 self.scroll_to_cursor()
233 }
234 }
235 Message::DeleteSelection => {
236 if self.is_grouping {
238 self.history.end_group();
239 self.is_grouping = false;
240 }
241
242 self.delete_selection();
244 self.reset_cursor_blink();
245 self.cache.clear();
246 self.scroll_to_cursor()
247 }
248 Message::Tick => {
249 if self.is_focused()
251 && self.has_canvas_focus
252 && self.last_blink.elapsed() >= CURSOR_BLINK_INTERVAL
253 {
254 self.cursor_visible = !self.cursor_visible;
255 self.last_blink = std::time::Instant::now();
256 self.cache.clear();
257 }
258
259 if !self.has_canvas_focus {
261 self.show_cursor = false;
262 }
263
264 Task::none()
265 }
266 Message::PageUp => {
267 self.page_up();
268 self.reset_cursor_blink();
269 self.scroll_to_cursor()
270 }
271 Message::PageDown => {
272 self.page_down();
273 self.reset_cursor_blink();
274 self.scroll_to_cursor()
275 }
276 Message::Home(shift_pressed) => {
277 if *shift_pressed {
278 if self.selection_start.is_none() {
280 self.selection_start = Some(self.cursor);
281 }
282 self.cursor.1 = 0; self.selection_end = Some(self.cursor);
284 } else {
285 self.clear_selection();
287 self.cursor.1 = 0;
288 }
289 self.reset_cursor_blink();
290 self.cache.clear();
291 Task::none()
292 }
293 Message::End(shift_pressed) => {
294 let line = self.cursor.0;
295 let line_len = self.buffer.line_len(line);
296
297 if *shift_pressed {
298 if self.selection_start.is_none() {
300 self.selection_start = Some(self.cursor);
301 }
302 self.cursor.1 = line_len; self.selection_end = Some(self.cursor);
304 } else {
305 self.clear_selection();
307 self.cursor.1 = line_len;
308 }
309 self.reset_cursor_blink();
310 self.cache.clear();
311 Task::none()
312 }
313 Message::CtrlHome => {
314 self.clear_selection();
316 self.cursor = (0, 0);
317 self.reset_cursor_blink();
318 self.cache.clear();
319 self.scroll_to_cursor()
320 }
321 Message::CtrlEnd => {
322 self.clear_selection();
324 let last_line = self.buffer.line_count().saturating_sub(1);
325 let last_col = self.buffer.line_len(last_line);
326 self.cursor = (last_line, last_col);
327 self.reset_cursor_blink();
328 self.cache.clear();
329 self.scroll_to_cursor()
330 }
331 Message::Scrolled(viewport) => {
332 self.viewport_scroll = viewport.absolute_offset().y;
334 let new_height = viewport.bounds().height;
335 let new_width = viewport.bounds().width;
336 if (self.viewport_height - new_height).abs() > 1.0
339 || (self.viewport_width - new_width).abs() > 1.0
340 {
341 self.cache.clear();
342 }
343 self.viewport_height = new_height;
344 self.viewport_width = new_width;
345 Task::none()
346 }
347 Message::Undo => {
348 if self.is_grouping {
350 self.history.end_group();
351 self.is_grouping = false;
352 }
353
354 if self.history.undo(&mut self.buffer, &mut self.cursor) {
355 self.clear_selection();
356 self.reset_cursor_blink();
357 self.refresh_search_matches_if_needed();
358 self.cache.clear();
359 self.scroll_to_cursor()
360 } else {
361 Task::none()
362 }
363 }
364 Message::Redo => {
365 if self.history.redo(&mut self.buffer, &mut self.cursor) {
366 self.clear_selection();
367 self.reset_cursor_blink();
368 self.refresh_search_matches_if_needed();
369 self.cache.clear();
370 self.scroll_to_cursor()
371 } else {
372 Task::none()
373 }
374 }
375 Message::OpenSearch => {
376 self.search_state.open_search();
377 self.cache.clear();
378
379 Task::batch([
381 focus(self.search_state.search_input_id.clone()),
382 select_all(self.search_state.search_input_id.clone()),
383 ])
384 }
385 Message::OpenSearchReplace => {
386 self.search_state.open_replace();
387 self.cache.clear();
388
389 Task::batch([
391 focus(self.search_state.search_input_id.clone()),
392 select_all(self.search_state.search_input_id.clone()),
393 ])
394 }
395 Message::CloseSearch => {
396 self.search_state.close();
397 self.cache.clear();
398 Task::none()
399 }
400 Message::SearchQueryChanged(query) => {
401 self.search_state.set_query(query.clone(), &self.buffer);
402 self.cache.clear();
403
404 if let Some(match_pos) = self.search_state.current_match() {
406 self.cursor = (match_pos.line, match_pos.col);
407 self.clear_selection();
408 return self.scroll_to_cursor();
409 }
410 Task::none()
411 }
412 Message::ReplaceQueryChanged(replace_text) => {
413 self.search_state.set_replace_with(replace_text.clone());
414 Task::none()
415 }
416 Message::ToggleCaseSensitive => {
417 self.search_state.toggle_case_sensitive(&self.buffer);
418 self.cache.clear();
419
420 if let Some(match_pos) = self.search_state.current_match() {
422 self.cursor = (match_pos.line, match_pos.col);
423 self.clear_selection();
424 return self.scroll_to_cursor();
425 }
426 Task::none()
427 }
428 Message::FindNext => {
429 if !self.search_state.matches.is_empty() {
430 self.search_state.next_match();
431 if let Some(match_pos) = self.search_state.current_match() {
432 self.cursor = (match_pos.line, match_pos.col);
433 self.clear_selection();
434 self.cache.clear();
435 return self.scroll_to_cursor();
436 }
437 }
438 Task::none()
439 }
440 Message::FindPrevious => {
441 if !self.search_state.matches.is_empty() {
442 self.search_state.previous_match();
443 if let Some(match_pos) = self.search_state.current_match() {
444 self.cursor = (match_pos.line, match_pos.col);
445 self.clear_selection();
446 self.cache.clear();
447 return self.scroll_to_cursor();
448 }
449 }
450 Task::none()
451 }
452 Message::ReplaceNext => {
453 if let Some(match_pos) = self.search_state.current_match() {
455 let query_len = self.search_state.query.chars().count();
456 let replace_text = self.search_state.replace_with.clone();
457
458 let mut cmd = ReplaceTextCommand::new(
460 &self.buffer,
461 (match_pos.line, match_pos.col),
462 query_len,
463 replace_text,
464 self.cursor,
465 );
466 cmd.execute(&mut self.buffer, &mut self.cursor);
467 self.history.push(Box::new(cmd));
468
469 self.search_state.update_matches(&self.buffer);
471
472 if !self.search_state.matches.is_empty()
474 && let Some(next_match) =
475 self.search_state.current_match()
476 {
477 self.cursor = (next_match.line, next_match.col);
478 }
479
480 self.clear_selection();
481 self.cache.clear();
482 return self.scroll_to_cursor();
483 }
484 Task::none()
485 }
486 Message::ReplaceAll => {
487 if !self.search_state.matches.is_empty() {
489 let query_len = self.search_state.query.chars().count();
490 let replace_text = self.search_state.replace_with.clone();
491
492 let mut composite =
494 CompositeCommand::new("Replace All".to_string());
495
496 for match_pos in self.search_state.matches.iter().rev() {
498 let cmd = ReplaceTextCommand::new(
499 &self.buffer,
500 (match_pos.line, match_pos.col),
501 query_len,
502 replace_text.clone(),
503 self.cursor,
504 );
505 composite.add(Box::new(cmd));
506 }
507
508 composite.execute(&mut self.buffer, &mut self.cursor);
510 self.history.push(Box::new(composite));
511
512 self.search_state.update_matches(&self.buffer);
514
515 self.clear_selection();
516 self.cache.clear();
517 return self.scroll_to_cursor();
518 }
519 Task::none()
520 }
521 Message::SearchDialogTab => {
522 self.search_state.focus_next_field();
524
525 match self.search_state.focused_field {
527 crate::canvas_editor::search::SearchFocusedField::Search => {
528 focus(self.search_state.search_input_id.clone())
529 }
530 crate::canvas_editor::search::SearchFocusedField::Replace => {
531 focus(self.search_state.replace_input_id.clone())
532 }
533 }
534 }
535 Message::SearchDialogShiftTab => {
536 self.search_state.focus_previous_field();
538
539 match self.search_state.focused_field {
541 crate::canvas_editor::search::SearchFocusedField::Search => {
542 focus(self.search_state.search_input_id.clone())
543 }
544 crate::canvas_editor::search::SearchFocusedField::Replace => {
545 focus(self.search_state.replace_input_id.clone())
546 }
547 }
548 }
549 Message::CanvasFocusGained => {
550 self.has_canvas_focus = true;
551 self.show_cursor = true;
552 self.reset_cursor_blink();
553 self.cache.clear();
554 Task::none()
555 }
556 Message::CanvasFocusLost => {
557 self.has_canvas_focus = false;
558 self.show_cursor = false;
559 self.cache.clear();
560 Task::none()
561 }
562 }
563 }
564}
565
566#[cfg(test)]
567mod tests {
568 use super::*;
569 use crate::canvas_editor::ArrowDirection;
570
571 #[test]
572 fn test_new_canvas_editor() {
573 let editor = CodeEditor::new("line1\nline2", "py");
574 assert_eq!(editor.cursor, (0, 0));
575 }
576
577 #[test]
578 fn test_home_key() {
579 let mut editor = CodeEditor::new("hello world", "py");
580 editor.cursor = (0, 5); let _ = editor.update(&Message::Home(false));
582 assert_eq!(editor.cursor, (0, 0));
583 }
584
585 #[test]
586 fn test_end_key() {
587 let mut editor = CodeEditor::new("hello world", "py");
588 editor.cursor = (0, 0);
589 let _ = editor.update(&Message::End(false));
590 assert_eq!(editor.cursor, (0, 11)); }
592
593 #[test]
594 fn test_arrow_key_with_shift_creates_selection() {
595 let mut editor = CodeEditor::new("hello world", "py");
596 editor.cursor = (0, 0);
597
598 let _ = editor.update(&Message::ArrowKey(ArrowDirection::Right, true));
600 assert!(editor.selection_start.is_some());
601 assert!(editor.selection_end.is_some());
602 }
603
604 #[test]
605 fn test_arrow_key_without_shift_clears_selection() {
606 let mut editor = CodeEditor::new("hello world", "py");
607 editor.selection_start = Some((0, 0));
608 editor.selection_end = Some((0, 5));
609
610 let _ = editor.update(&Message::ArrowKey(ArrowDirection::Right, false));
612 assert_eq!(editor.selection_start, None);
613 assert_eq!(editor.selection_end, None);
614 }
615
616 #[test]
617 fn test_typing_with_selection() {
618 let mut editor = CodeEditor::new("hello world", "py");
619 editor.selection_start = Some((0, 0));
620 editor.selection_end = Some((0, 5));
621
622 let _ = editor.update(&Message::CharacterInput('X'));
623 assert_eq!(editor.buffer.line(0), "Xhello world");
626 }
627
628 #[test]
629 fn test_ctrl_home() {
630 let mut editor = CodeEditor::new("line1\nline2\nline3", "py");
631 editor.cursor = (2, 5); let _ = editor.update(&Message::CtrlHome);
633 assert_eq!(editor.cursor, (0, 0)); }
635
636 #[test]
637 fn test_ctrl_end() {
638 let mut editor = CodeEditor::new("line1\nline2\nline3", "py");
639 editor.cursor = (0, 0); let _ = editor.update(&Message::CtrlEnd);
641 assert_eq!(editor.cursor, (2, 5)); }
643
644 #[test]
645 fn test_ctrl_home_clears_selection() {
646 let mut editor = CodeEditor::new("line1\nline2\nline3", "py");
647 editor.cursor = (2, 5);
648 editor.selection_start = Some((0, 0));
649 editor.selection_end = Some((2, 5));
650
651 let _ = editor.update(&Message::CtrlHome);
652 assert_eq!(editor.cursor, (0, 0));
653 assert_eq!(editor.selection_start, None);
654 assert_eq!(editor.selection_end, None);
655 }
656
657 #[test]
658 fn test_ctrl_end_clears_selection() {
659 let mut editor = CodeEditor::new("line1\nline2\nline3", "py");
660 editor.cursor = (0, 0);
661 editor.selection_start = Some((0, 0));
662 editor.selection_end = Some((1, 3));
663
664 let _ = editor.update(&Message::CtrlEnd);
665 assert_eq!(editor.cursor, (2, 5));
666 assert_eq!(editor.selection_start, None);
667 assert_eq!(editor.selection_end, None);
668 }
669
670 #[test]
671 fn test_delete_selection_message() {
672 let mut editor = CodeEditor::new("hello world", "py");
673 editor.cursor = (0, 0);
674 editor.selection_start = Some((0, 0));
675 editor.selection_end = Some((0, 5));
676
677 let _ = editor.update(&Message::DeleteSelection);
678 assert_eq!(editor.buffer.line(0), " world");
679 assert_eq!(editor.cursor, (0, 0));
680 assert_eq!(editor.selection_start, None);
681 assert_eq!(editor.selection_end, None);
682 }
683
684 #[test]
685 fn test_delete_selection_multiline() {
686 let mut editor = CodeEditor::new("line1\nline2\nline3", "py");
687 editor.cursor = (0, 2);
688 editor.selection_start = Some((0, 2));
689 editor.selection_end = Some((2, 2));
690
691 let _ = editor.update(&Message::DeleteSelection);
692 assert_eq!(editor.buffer.line(0), "line3");
693 assert_eq!(editor.cursor, (0, 2));
694 assert_eq!(editor.selection_start, None);
695 }
696
697 #[test]
698 fn test_delete_selection_no_selection() {
699 let mut editor = CodeEditor::new("hello world", "py");
700 editor.cursor = (0, 5);
701
702 let _ = editor.update(&Message::DeleteSelection);
703 assert_eq!(editor.buffer.line(0), "hello world");
705 assert_eq!(editor.cursor, (0, 5));
706 }
707
708 #[test]
709 fn test_undo_char_insert() {
710 let mut editor = CodeEditor::new("hello", "py");
711 editor.cursor = (0, 5);
712
713 let _ = editor.update(&Message::CharacterInput('!'));
715 assert_eq!(editor.buffer.line(0), "hello!");
716 assert_eq!(editor.cursor, (0, 6));
717
718 editor.history.end_group();
720 let _ = editor.update(&Message::Undo);
721 assert_eq!(editor.buffer.line(0), "hello");
722 assert_eq!(editor.cursor, (0, 5));
723 }
724
725 #[test]
726 fn test_undo_redo_char_insert() {
727 let mut editor = CodeEditor::new("hello", "py");
728 editor.cursor = (0, 5);
729
730 let _ = editor.update(&Message::CharacterInput('!'));
732 editor.history.end_group();
733
734 let _ = editor.update(&Message::Undo);
736 assert_eq!(editor.buffer.line(0), "hello");
737
738 let _ = editor.update(&Message::Redo);
740 assert_eq!(editor.buffer.line(0), "hello!");
741 assert_eq!(editor.cursor, (0, 6));
742 }
743
744 #[test]
745 fn test_undo_backspace() {
746 let mut editor = CodeEditor::new("hello", "py");
747 editor.cursor = (0, 5);
748
749 let _ = editor.update(&Message::Backspace);
751 assert_eq!(editor.buffer.line(0), "hell");
752 assert_eq!(editor.cursor, (0, 4));
753
754 let _ = editor.update(&Message::Undo);
756 assert_eq!(editor.buffer.line(0), "hello");
757 assert_eq!(editor.cursor, (0, 5));
758 }
759
760 #[test]
761 fn test_undo_newline() {
762 let mut editor = CodeEditor::new("hello world", "py");
763 editor.cursor = (0, 5);
764
765 let _ = editor.update(&Message::Enter);
767 assert_eq!(editor.buffer.line(0), "hello");
768 assert_eq!(editor.buffer.line(1), " world");
769 assert_eq!(editor.cursor, (1, 0));
770
771 let _ = editor.update(&Message::Undo);
773 assert_eq!(editor.buffer.line(0), "hello world");
774 assert_eq!(editor.cursor, (0, 5));
775 }
776
777 #[test]
778 fn test_undo_grouped_typing() {
779 let mut editor = CodeEditor::new("hello", "py");
780 editor.cursor = (0, 5);
781
782 let _ = editor.update(&Message::CharacterInput(' '));
784 let _ = editor.update(&Message::CharacterInput('w'));
785 let _ = editor.update(&Message::CharacterInput('o'));
786 let _ = editor.update(&Message::CharacterInput('r'));
787 let _ = editor.update(&Message::CharacterInput('l'));
788 let _ = editor.update(&Message::CharacterInput('d'));
789
790 assert_eq!(editor.buffer.line(0), "hello world");
791
792 editor.history.end_group();
794
795 let _ = editor.update(&Message::Undo);
797 assert_eq!(editor.buffer.line(0), "hello");
798 assert_eq!(editor.cursor, (0, 5));
799 }
800
801 #[test]
802 fn test_navigation_ends_grouping() {
803 let mut editor = CodeEditor::new("hello", "py");
804 editor.cursor = (0, 5);
805
806 let _ = editor.update(&Message::CharacterInput('!'));
808 assert!(editor.is_grouping);
809
810 let _ = editor.update(&Message::ArrowKey(ArrowDirection::Left, false));
812 assert!(!editor.is_grouping);
813
814 let _ = editor.update(&Message::CharacterInput('?'));
816 assert!(editor.is_grouping);
817
818 editor.history.end_group();
819
820 let _ = editor.update(&Message::Undo);
822 assert_eq!(editor.buffer.line(0), "hello!");
823
824 let _ = editor.update(&Message::Undo);
825 assert_eq!(editor.buffer.line(0), "hello");
826 }
827
828 #[test]
829 fn test_multiple_undo_redo() {
830 let mut editor = CodeEditor::new("a", "py");
831 editor.cursor = (0, 1);
832
833 let _ = editor.update(&Message::CharacterInput('b'));
835 editor.history.end_group();
836
837 let _ = editor.update(&Message::CharacterInput('c'));
838 editor.history.end_group();
839
840 let _ = editor.update(&Message::CharacterInput('d'));
841 editor.history.end_group();
842
843 assert_eq!(editor.buffer.line(0), "abcd");
844
845 let _ = editor.update(&Message::Undo);
847 assert_eq!(editor.buffer.line(0), "abc");
848
849 let _ = editor.update(&Message::Undo);
850 assert_eq!(editor.buffer.line(0), "ab");
851
852 let _ = editor.update(&Message::Undo);
853 assert_eq!(editor.buffer.line(0), "a");
854
855 let _ = editor.update(&Message::Redo);
857 assert_eq!(editor.buffer.line(0), "ab");
858
859 let _ = editor.update(&Message::Redo);
860 assert_eq!(editor.buffer.line(0), "abc");
861
862 let _ = editor.update(&Message::Redo);
863 assert_eq!(editor.buffer.line(0), "abcd");
864 }
865
866 #[test]
867 fn test_delete_key_with_selection() {
868 let mut editor = CodeEditor::new("hello world", "py");
869 editor.selection_start = Some((0, 0));
870 editor.selection_end = Some((0, 5));
871 editor.cursor = (0, 5);
872
873 let _ = editor.update(&Message::Delete);
874
875 assert_eq!(editor.buffer.line(0), " world");
876 assert_eq!(editor.cursor, (0, 0));
877 assert_eq!(editor.selection_start, None);
878 assert_eq!(editor.selection_end, None);
879 }
880
881 #[test]
882 fn test_delete_key_without_selection() {
883 let mut editor = CodeEditor::new("hello", "py");
884 editor.cursor = (0, 0);
885
886 let _ = editor.update(&Message::Delete);
887
888 assert_eq!(editor.buffer.line(0), "ello");
890 assert_eq!(editor.cursor, (0, 0));
891 }
892
893 #[test]
894 fn test_backspace_with_selection() {
895 let mut editor = CodeEditor::new("hello world", "py");
896 editor.selection_start = Some((0, 6));
897 editor.selection_end = Some((0, 11));
898 editor.cursor = (0, 11);
899
900 let _ = editor.update(&Message::Backspace);
901
902 assert_eq!(editor.buffer.line(0), "hello ");
903 assert_eq!(editor.cursor, (0, 6));
904 assert_eq!(editor.selection_start, None);
905 assert_eq!(editor.selection_end, None);
906 }
907
908 #[test]
909 fn test_backspace_without_selection() {
910 let mut editor = CodeEditor::new("hello", "py");
911 editor.cursor = (0, 5);
912
913 let _ = editor.update(&Message::Backspace);
914
915 assert_eq!(editor.buffer.line(0), "hell");
917 assert_eq!(editor.cursor, (0, 4));
918 }
919
920 #[test]
921 fn test_delete_multiline_selection() {
922 let mut editor = CodeEditor::new("line1\nline2\nline3", "py");
923 editor.selection_start = Some((0, 2));
924 editor.selection_end = Some((2, 2));
925 editor.cursor = (2, 2);
926
927 let _ = editor.update(&Message::Delete);
928
929 assert_eq!(editor.buffer.line(0), "line3");
930 assert_eq!(editor.cursor, (0, 2));
931 assert_eq!(editor.selection_start, None);
932 }
933
934 #[test]
935 fn test_canvas_focus_gained() {
936 let mut editor = CodeEditor::new("hello world", "py");
937 assert!(!editor.has_canvas_focus);
938 assert!(!editor.show_cursor);
939
940 let _ = editor.update(&Message::CanvasFocusGained);
941
942 assert!(editor.has_canvas_focus);
943 assert!(editor.show_cursor);
944 }
945
946 #[test]
947 fn test_canvas_focus_lost() {
948 let mut editor = CodeEditor::new("hello world", "py");
949 editor.has_canvas_focus = true;
950 editor.show_cursor = true;
951
952 let _ = editor.update(&Message::CanvasFocusLost);
953
954 assert!(!editor.has_canvas_focus);
955 assert!(!editor.show_cursor);
956 }
957
958 #[test]
959 fn test_mouse_click_gains_focus() {
960 let mut editor = CodeEditor::new("hello world", "py");
961 editor.has_canvas_focus = false;
962 editor.show_cursor = false;
963
964 let _ =
965 editor.update(&Message::MouseClick(iced::Point::new(100.0, 10.0)));
966
967 assert!(editor.has_canvas_focus);
968 assert!(editor.show_cursor);
969 }
970}