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 Task::none()
196 }
197 Message::MouseDrag(point) => {
198 if self.is_dragging {
199 self.handle_mouse_drag(*point);
200 self.cache.clear();
201 }
202 Task::none()
203 }
204 Message::MouseRelease => {
205 self.is_dragging = false;
206 Task::none()
207 }
208 Message::Copy => self.copy_selection(),
209 Message::Paste(text) => {
210 if self.is_grouping {
212 self.history.end_group();
213 self.is_grouping = false;
214 }
215
216 if text.is_empty() {
218 iced::clipboard::read().and_then(|clipboard_text| {
220 Task::done(Message::Paste(clipboard_text))
221 })
222 } else {
223 self.paste_text(text);
225 self.refresh_search_matches_if_needed();
226 self.cache.clear();
227 self.scroll_to_cursor()
228 }
229 }
230 Message::DeleteSelection => {
231 if self.is_grouping {
233 self.history.end_group();
234 self.is_grouping = false;
235 }
236
237 self.delete_selection();
239 self.reset_cursor_blink();
240 self.cache.clear();
241 self.scroll_to_cursor()
242 }
243 Message::Tick => {
244 if self.last_blink.elapsed() >= CURSOR_BLINK_INTERVAL {
246 self.cursor_visible = !self.cursor_visible;
247 self.last_blink = std::time::Instant::now();
248 self.cache.clear();
249 }
250 Task::none()
251 }
252 Message::PageUp => {
253 self.page_up();
254 self.reset_cursor_blink();
255 self.scroll_to_cursor()
256 }
257 Message::PageDown => {
258 self.page_down();
259 self.reset_cursor_blink();
260 self.scroll_to_cursor()
261 }
262 Message::Home(shift_pressed) => {
263 if *shift_pressed {
264 if self.selection_start.is_none() {
266 self.selection_start = Some(self.cursor);
267 }
268 self.cursor.1 = 0; self.selection_end = Some(self.cursor);
270 } else {
271 self.clear_selection();
273 self.cursor.1 = 0;
274 }
275 self.reset_cursor_blink();
276 self.cache.clear();
277 Task::none()
278 }
279 Message::End(shift_pressed) => {
280 let line = self.cursor.0;
281 let line_len = self.buffer.line_len(line);
282
283 if *shift_pressed {
284 if self.selection_start.is_none() {
286 self.selection_start = Some(self.cursor);
287 }
288 self.cursor.1 = line_len; self.selection_end = Some(self.cursor);
290 } else {
291 self.clear_selection();
293 self.cursor.1 = line_len;
294 }
295 self.reset_cursor_blink();
296 self.cache.clear();
297 Task::none()
298 }
299 Message::CtrlHome => {
300 self.clear_selection();
302 self.cursor = (0, 0);
303 self.reset_cursor_blink();
304 self.cache.clear();
305 self.scroll_to_cursor()
306 }
307 Message::CtrlEnd => {
308 self.clear_selection();
310 let last_line = self.buffer.line_count().saturating_sub(1);
311 let last_col = self.buffer.line_len(last_line);
312 self.cursor = (last_line, last_col);
313 self.reset_cursor_blink();
314 self.cache.clear();
315 self.scroll_to_cursor()
316 }
317 Message::Scrolled(viewport) => {
318 self.viewport_scroll = viewport.absolute_offset().y;
320 let new_height = viewport.bounds().height;
321 let new_width = viewport.bounds().width;
322 if (self.viewport_height - new_height).abs() > 1.0
325 || (self.viewport_width - new_width).abs() > 1.0
326 {
327 self.cache.clear();
328 }
329 self.viewport_height = new_height;
330 self.viewport_width = new_width;
331 Task::none()
332 }
333 Message::Undo => {
334 if self.is_grouping {
336 self.history.end_group();
337 self.is_grouping = false;
338 }
339
340 if self.history.undo(&mut self.buffer, &mut self.cursor) {
341 self.clear_selection();
342 self.reset_cursor_blink();
343 self.refresh_search_matches_if_needed();
344 self.cache.clear();
345 self.scroll_to_cursor()
346 } else {
347 Task::none()
348 }
349 }
350 Message::Redo => {
351 if self.history.redo(&mut self.buffer, &mut self.cursor) {
352 self.clear_selection();
353 self.reset_cursor_blink();
354 self.refresh_search_matches_if_needed();
355 self.cache.clear();
356 self.scroll_to_cursor()
357 } else {
358 Task::none()
359 }
360 }
361 Message::OpenSearch => {
362 self.search_state.open_search();
363 self.cache.clear();
364
365 Task::batch([
367 focus(self.search_state.search_input_id.clone()),
368 select_all(self.search_state.search_input_id.clone()),
369 ])
370 }
371 Message::OpenSearchReplace => {
372 self.search_state.open_replace();
373 self.cache.clear();
374
375 Task::batch([
377 focus(self.search_state.search_input_id.clone()),
378 select_all(self.search_state.search_input_id.clone()),
379 ])
380 }
381 Message::CloseSearch => {
382 self.search_state.close();
383 self.cache.clear();
384 Task::none()
385 }
386 Message::SearchQueryChanged(query) => {
387 self.search_state.set_query(query.clone(), &self.buffer);
388 self.cache.clear();
389
390 if let Some(match_pos) = self.search_state.current_match() {
392 self.cursor = (match_pos.line, match_pos.col);
393 self.clear_selection();
394 return self.scroll_to_cursor();
395 }
396 Task::none()
397 }
398 Message::ReplaceQueryChanged(replace_text) => {
399 self.search_state.set_replace_with(replace_text.clone());
400 Task::none()
401 }
402 Message::ToggleCaseSensitive => {
403 self.search_state.toggle_case_sensitive(&self.buffer);
404 self.cache.clear();
405
406 if let Some(match_pos) = self.search_state.current_match() {
408 self.cursor = (match_pos.line, match_pos.col);
409 self.clear_selection();
410 return self.scroll_to_cursor();
411 }
412 Task::none()
413 }
414 Message::FindNext => {
415 if !self.search_state.matches.is_empty() {
416 self.search_state.next_match();
417 if let Some(match_pos) = self.search_state.current_match() {
418 self.cursor = (match_pos.line, match_pos.col);
419 self.clear_selection();
420 self.cache.clear();
421 return self.scroll_to_cursor();
422 }
423 }
424 Task::none()
425 }
426 Message::FindPrevious => {
427 if !self.search_state.matches.is_empty() {
428 self.search_state.previous_match();
429 if let Some(match_pos) = self.search_state.current_match() {
430 self.cursor = (match_pos.line, match_pos.col);
431 self.clear_selection();
432 self.cache.clear();
433 return self.scroll_to_cursor();
434 }
435 }
436 Task::none()
437 }
438 Message::ReplaceNext => {
439 if let Some(match_pos) = self.search_state.current_match() {
441 let query_len = self.search_state.query.chars().count();
442 let replace_text = self.search_state.replace_with.clone();
443
444 let mut cmd = ReplaceTextCommand::new(
446 &self.buffer,
447 (match_pos.line, match_pos.col),
448 query_len,
449 replace_text,
450 self.cursor,
451 );
452 cmd.execute(&mut self.buffer, &mut self.cursor);
453 self.history.push(Box::new(cmd));
454
455 self.search_state.update_matches(&self.buffer);
457
458 if !self.search_state.matches.is_empty()
460 && let Some(next_match) =
461 self.search_state.current_match()
462 {
463 self.cursor = (next_match.line, next_match.col);
464 }
465
466 self.clear_selection();
467 self.cache.clear();
468 return self.scroll_to_cursor();
469 }
470 Task::none()
471 }
472 Message::ReplaceAll => {
473 if !self.search_state.matches.is_empty() {
475 let query_len = self.search_state.query.chars().count();
476 let replace_text = self.search_state.replace_with.clone();
477
478 let mut composite =
480 CompositeCommand::new("Replace All".to_string());
481
482 for match_pos in self.search_state.matches.iter().rev() {
484 let cmd = ReplaceTextCommand::new(
485 &self.buffer,
486 (match_pos.line, match_pos.col),
487 query_len,
488 replace_text.clone(),
489 self.cursor,
490 );
491 composite.add(Box::new(cmd));
492 }
493
494 composite.execute(&mut self.buffer, &mut self.cursor);
496 self.history.push(Box::new(composite));
497
498 self.search_state.update_matches(&self.buffer);
500
501 self.clear_selection();
502 self.cache.clear();
503 return self.scroll_to_cursor();
504 }
505 Task::none()
506 }
507 Message::SearchDialogTab => {
508 self.search_state.focus_next_field();
510
511 match self.search_state.focused_field {
513 crate::canvas_editor::search::SearchFocusedField::Search => {
514 focus(self.search_state.search_input_id.clone())
515 }
516 crate::canvas_editor::search::SearchFocusedField::Replace => {
517 focus(self.search_state.replace_input_id.clone())
518 }
519 }
520 }
521 Message::SearchDialogShiftTab => {
522 self.search_state.focus_previous_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 }
536 }
537}
538
539#[cfg(test)]
540mod tests {
541 use super::*;
542 use crate::canvas_editor::ArrowDirection;
543
544 #[test]
545 fn test_new_canvas_editor() {
546 let editor = CodeEditor::new("line1\nline2", "py");
547 assert_eq!(editor.cursor, (0, 0));
548 }
549
550 #[test]
551 fn test_home_key() {
552 let mut editor = CodeEditor::new("hello world", "py");
553 editor.cursor = (0, 5); let _ = editor.update(&Message::Home(false));
555 assert_eq!(editor.cursor, (0, 0));
556 }
557
558 #[test]
559 fn test_end_key() {
560 let mut editor = CodeEditor::new("hello world", "py");
561 editor.cursor = (0, 0);
562 let _ = editor.update(&Message::End(false));
563 assert_eq!(editor.cursor, (0, 11)); }
565
566 #[test]
567 fn test_arrow_key_with_shift_creates_selection() {
568 let mut editor = CodeEditor::new("hello world", "py");
569 editor.cursor = (0, 0);
570
571 let _ = editor.update(&Message::ArrowKey(ArrowDirection::Right, true));
573 assert!(editor.selection_start.is_some());
574 assert!(editor.selection_end.is_some());
575 }
576
577 #[test]
578 fn test_arrow_key_without_shift_clears_selection() {
579 let mut editor = CodeEditor::new("hello world", "py");
580 editor.selection_start = Some((0, 0));
581 editor.selection_end = Some((0, 5));
582
583 let _ = editor.update(&Message::ArrowKey(ArrowDirection::Right, false));
585 assert_eq!(editor.selection_start, None);
586 assert_eq!(editor.selection_end, None);
587 }
588
589 #[test]
590 fn test_typing_with_selection() {
591 let mut editor = CodeEditor::new("hello world", "py");
592 editor.selection_start = Some((0, 0));
593 editor.selection_end = Some((0, 5));
594
595 let _ = editor.update(&Message::CharacterInput('X'));
596 assert_eq!(editor.buffer.line(0), "Xhello world");
599 }
600
601 #[test]
602 fn test_ctrl_home() {
603 let mut editor = CodeEditor::new("line1\nline2\nline3", "py");
604 editor.cursor = (2, 5); let _ = editor.update(&Message::CtrlHome);
606 assert_eq!(editor.cursor, (0, 0)); }
608
609 #[test]
610 fn test_ctrl_end() {
611 let mut editor = CodeEditor::new("line1\nline2\nline3", "py");
612 editor.cursor = (0, 0); let _ = editor.update(&Message::CtrlEnd);
614 assert_eq!(editor.cursor, (2, 5)); }
616
617 #[test]
618 fn test_ctrl_home_clears_selection() {
619 let mut editor = CodeEditor::new("line1\nline2\nline3", "py");
620 editor.cursor = (2, 5);
621 editor.selection_start = Some((0, 0));
622 editor.selection_end = Some((2, 5));
623
624 let _ = editor.update(&Message::CtrlHome);
625 assert_eq!(editor.cursor, (0, 0));
626 assert_eq!(editor.selection_start, None);
627 assert_eq!(editor.selection_end, None);
628 }
629
630 #[test]
631 fn test_ctrl_end_clears_selection() {
632 let mut editor = CodeEditor::new("line1\nline2\nline3", "py");
633 editor.cursor = (0, 0);
634 editor.selection_start = Some((0, 0));
635 editor.selection_end = Some((1, 3));
636
637 let _ = editor.update(&Message::CtrlEnd);
638 assert_eq!(editor.cursor, (2, 5));
639 assert_eq!(editor.selection_start, None);
640 assert_eq!(editor.selection_end, None);
641 }
642
643 #[test]
644 fn test_delete_selection_message() {
645 let mut editor = CodeEditor::new("hello world", "py");
646 editor.cursor = (0, 0);
647 editor.selection_start = Some((0, 0));
648 editor.selection_end = Some((0, 5));
649
650 let _ = editor.update(&Message::DeleteSelection);
651 assert_eq!(editor.buffer.line(0), " world");
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_delete_selection_multiline() {
659 let mut editor = CodeEditor::new("line1\nline2\nline3", "py");
660 editor.cursor = (0, 2);
661 editor.selection_start = Some((0, 2));
662 editor.selection_end = Some((2, 2));
663
664 let _ = editor.update(&Message::DeleteSelection);
665 assert_eq!(editor.buffer.line(0), "line3");
666 assert_eq!(editor.cursor, (0, 2));
667 assert_eq!(editor.selection_start, None);
668 }
669
670 #[test]
671 fn test_delete_selection_no_selection() {
672 let mut editor = CodeEditor::new("hello world", "py");
673 editor.cursor = (0, 5);
674
675 let _ = editor.update(&Message::DeleteSelection);
676 assert_eq!(editor.buffer.line(0), "hello world");
678 assert_eq!(editor.cursor, (0, 5));
679 }
680
681 #[test]
682 fn test_undo_char_insert() {
683 let mut editor = CodeEditor::new("hello", "py");
684 editor.cursor = (0, 5);
685
686 let _ = editor.update(&Message::CharacterInput('!'));
688 assert_eq!(editor.buffer.line(0), "hello!");
689 assert_eq!(editor.cursor, (0, 6));
690
691 editor.history.end_group();
693 let _ = editor.update(&Message::Undo);
694 assert_eq!(editor.buffer.line(0), "hello");
695 assert_eq!(editor.cursor, (0, 5));
696 }
697
698 #[test]
699 fn test_undo_redo_char_insert() {
700 let mut editor = CodeEditor::new("hello", "py");
701 editor.cursor = (0, 5);
702
703 let _ = editor.update(&Message::CharacterInput('!'));
705 editor.history.end_group();
706
707 let _ = editor.update(&Message::Undo);
709 assert_eq!(editor.buffer.line(0), "hello");
710
711 let _ = editor.update(&Message::Redo);
713 assert_eq!(editor.buffer.line(0), "hello!");
714 assert_eq!(editor.cursor, (0, 6));
715 }
716
717 #[test]
718 fn test_undo_backspace() {
719 let mut editor = CodeEditor::new("hello", "py");
720 editor.cursor = (0, 5);
721
722 let _ = editor.update(&Message::Backspace);
724 assert_eq!(editor.buffer.line(0), "hell");
725 assert_eq!(editor.cursor, (0, 4));
726
727 let _ = editor.update(&Message::Undo);
729 assert_eq!(editor.buffer.line(0), "hello");
730 assert_eq!(editor.cursor, (0, 5));
731 }
732
733 #[test]
734 fn test_undo_newline() {
735 let mut editor = CodeEditor::new("hello world", "py");
736 editor.cursor = (0, 5);
737
738 let _ = editor.update(&Message::Enter);
740 assert_eq!(editor.buffer.line(0), "hello");
741 assert_eq!(editor.buffer.line(1), " world");
742 assert_eq!(editor.cursor, (1, 0));
743
744 let _ = editor.update(&Message::Undo);
746 assert_eq!(editor.buffer.line(0), "hello world");
747 assert_eq!(editor.cursor, (0, 5));
748 }
749
750 #[test]
751 fn test_undo_grouped_typing() {
752 let mut editor = CodeEditor::new("hello", "py");
753 editor.cursor = (0, 5);
754
755 let _ = editor.update(&Message::CharacterInput(' '));
757 let _ = editor.update(&Message::CharacterInput('w'));
758 let _ = editor.update(&Message::CharacterInput('o'));
759 let _ = editor.update(&Message::CharacterInput('r'));
760 let _ = editor.update(&Message::CharacterInput('l'));
761 let _ = editor.update(&Message::CharacterInput('d'));
762
763 assert_eq!(editor.buffer.line(0), "hello world");
764
765 editor.history.end_group();
767
768 let _ = editor.update(&Message::Undo);
770 assert_eq!(editor.buffer.line(0), "hello");
771 assert_eq!(editor.cursor, (0, 5));
772 }
773
774 #[test]
775 fn test_navigation_ends_grouping() {
776 let mut editor = CodeEditor::new("hello", "py");
777 editor.cursor = (0, 5);
778
779 let _ = editor.update(&Message::CharacterInput('!'));
781 assert!(editor.is_grouping);
782
783 let _ = editor.update(&Message::ArrowKey(ArrowDirection::Left, false));
785 assert!(!editor.is_grouping);
786
787 let _ = editor.update(&Message::CharacterInput('?'));
789 assert!(editor.is_grouping);
790
791 editor.history.end_group();
792
793 let _ = editor.update(&Message::Undo);
795 assert_eq!(editor.buffer.line(0), "hello!");
796
797 let _ = editor.update(&Message::Undo);
798 assert_eq!(editor.buffer.line(0), "hello");
799 }
800
801 #[test]
802 fn test_multiple_undo_redo() {
803 let mut editor = CodeEditor::new("a", "py");
804 editor.cursor = (0, 1);
805
806 let _ = editor.update(&Message::CharacterInput('b'));
808 editor.history.end_group();
809
810 let _ = editor.update(&Message::CharacterInput('c'));
811 editor.history.end_group();
812
813 let _ = editor.update(&Message::CharacterInput('d'));
814 editor.history.end_group();
815
816 assert_eq!(editor.buffer.line(0), "abcd");
817
818 let _ = editor.update(&Message::Undo);
820 assert_eq!(editor.buffer.line(0), "abc");
821
822 let _ = editor.update(&Message::Undo);
823 assert_eq!(editor.buffer.line(0), "ab");
824
825 let _ = editor.update(&Message::Undo);
826 assert_eq!(editor.buffer.line(0), "a");
827
828 let _ = editor.update(&Message::Redo);
830 assert_eq!(editor.buffer.line(0), "ab");
831
832 let _ = editor.update(&Message::Redo);
833 assert_eq!(editor.buffer.line(0), "abc");
834
835 let _ = editor.update(&Message::Redo);
836 assert_eq!(editor.buffer.line(0), "abcd");
837 }
838
839 #[test]
840 fn test_delete_key_with_selection() {
841 let mut editor = CodeEditor::new("hello world", "py");
842 editor.selection_start = Some((0, 0));
843 editor.selection_end = Some((0, 5));
844 editor.cursor = (0, 5);
845
846 let _ = editor.update(&Message::Delete);
847
848 assert_eq!(editor.buffer.line(0), " world");
849 assert_eq!(editor.cursor, (0, 0));
850 assert_eq!(editor.selection_start, None);
851 assert_eq!(editor.selection_end, None);
852 }
853
854 #[test]
855 fn test_delete_key_without_selection() {
856 let mut editor = CodeEditor::new("hello", "py");
857 editor.cursor = (0, 0);
858
859 let _ = editor.update(&Message::Delete);
860
861 assert_eq!(editor.buffer.line(0), "ello");
863 assert_eq!(editor.cursor, (0, 0));
864 }
865
866 #[test]
867 fn test_backspace_with_selection() {
868 let mut editor = CodeEditor::new("hello world", "py");
869 editor.selection_start = Some((0, 6));
870 editor.selection_end = Some((0, 11));
871 editor.cursor = (0, 11);
872
873 let _ = editor.update(&Message::Backspace);
874
875 assert_eq!(editor.buffer.line(0), "hello ");
876 assert_eq!(editor.cursor, (0, 6));
877 assert_eq!(editor.selection_start, None);
878 assert_eq!(editor.selection_end, None);
879 }
880
881 #[test]
882 fn test_backspace_without_selection() {
883 let mut editor = CodeEditor::new("hello", "py");
884 editor.cursor = (0, 5);
885
886 let _ = editor.update(&Message::Backspace);
887
888 assert_eq!(editor.buffer.line(0), "hell");
890 assert_eq!(editor.cursor, (0, 4));
891 }
892
893 #[test]
894 fn test_delete_multiline_selection() {
895 let mut editor = CodeEditor::new("line1\nline2\nline3", "py");
896 editor.selection_start = Some((0, 2));
897 editor.selection_end = Some((2, 2));
898 editor.cursor = (2, 2);
899
900 let _ = editor.update(&Message::Delete);
901
902 assert_eq!(editor.buffer.line(0), "line3");
903 assert_eq!(editor.cursor, (0, 2));
904 assert_eq!(editor.selection_start, None);
905 }
906}