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