1use crate::editor::{Editor};
2use crate::selection::{Selection};
3use crate::code::{EditKind};
4
5pub trait Action {
6 fn apply(&mut self, editor: &mut Editor);
7}
8
9
10pub struct MoveRight {
17 pub shift: bool,
18}
19
20impl Action for MoveRight {
21 fn apply(&mut self, editor: &mut Editor) {
22 let cursor = editor.get_cursor();
23
24 if !self.shift {
25 if let Some(sel) = editor.get_selection() {
26 if !sel.is_empty() {
27 let (_, end) = sel.sorted();
28 editor.set_cursor(end);
29 editor.clear_selection();
30 return;
31 }
32 }
33 }
34
35 if cursor < editor.code_mut().len() {
36 let new_cursor = cursor.saturating_add(1);
37 if self.shift {
38 editor.extend_selection(new_cursor);
39 } else {
40 editor.clear_selection();
41 }
42 editor.set_cursor(new_cursor);
43 }
44 }
45}
46
47pub struct MoveLeft {
54 pub shift: bool,
55}
56
57impl Action for MoveLeft {
58 fn apply(&mut self, editor: &mut Editor) {
59 let cursor = editor.get_cursor();
60
61 if !self.shift {
62 if let Some(sel) = editor.get_selection() {
63 if !sel.is_empty() {
64 let (start, _) = sel.sorted();
65 editor.set_cursor(start);
66 editor.clear_selection();
67 return;
68 }
69 }
70 }
71
72 if cursor > 0 {
73 let new_cursor = cursor.saturating_sub(1);
74 if self.shift {
75 editor.extend_selection(new_cursor);
76 } else {
77 editor.clear_selection();
78 }
79 editor.set_cursor(new_cursor);
80 }
81 }
82}
83
84pub struct MoveUp {
90 pub shift: bool,
91}
92
93impl Action for MoveUp {
94 fn apply(&mut self, editor: &mut Editor) {
95 let cursor = editor.get_cursor();
96 let code = editor.code_mut();
97 let (row, col) = code.point(cursor);
98
99 if row == 0 { return }
100
101 let prev_start = code.line_to_char(row - 1);
102 let prev_len = code.line_len(row - 1);
103 let new_col = col.min(prev_len);
104 let new_cursor = prev_start + new_col;
105
106 if self.shift {
108 editor.extend_selection(new_cursor);
109 } else {
110 editor.clear_selection();
111 }
112
113 editor.set_cursor(new_cursor);
115 }
116}
117
118pub struct MoveDown {
125 pub shift: bool,
126}
127
128impl Action for MoveDown {
129 fn apply(&mut self, editor: &mut Editor) {
130 let cursor = editor.get_cursor();
131 let code = editor.code_mut();
132 let (row, col) = code.point(cursor);
133 let is_last_line = row + 1 >= code.len_lines();
134 if is_last_line { return }
135
136 let next_start = code.line_to_char(row + 1);
137 let next_len = code.line_len(row + 1);
138 let new_col = col.min(next_len);
139 let new_cursor = next_start + new_col;
140
141 if self.shift {
143 editor.extend_selection(new_cursor);
144 } else {
145 editor.clear_selection();
146 }
147
148 editor.set_cursor(new_cursor);
150 }
151}
152
153pub struct InsertText {
155 pub text: String,
156}
157
158impl Action for InsertText {
159 fn apply(&mut self, editor: &mut Editor) {
160 let mut cursor = editor.get_cursor();
162 let mut selection = editor.get_selection();
163
164 let code = editor.code_mut();
166 code.tx();
167 code.set_state_before(cursor, selection);
168
169 if let Some(sel) = &selection {
171 if !sel.is_empty() {
172 let (start, end) = sel.sorted();
173 code.remove(start, end);
174 cursor = start;
175 }
176 }
177 selection = None;
178
179 code.insert(cursor, &self.text);
181 cursor += self.text.chars().count();
182
183 code.set_state_after(cursor, selection);
185 code.commit();
186
187 editor.set_cursor(cursor);
188 editor.set_selection(selection);
189 editor.reset_highlight_cache();
190 }
191}
192
193pub struct InsertNewline;
198
199impl Action for InsertNewline {
200 fn apply(&mut self, editor: &mut Editor) {
201 let cursor = editor.get_cursor();
203 let code = editor.code_mut();
204 let (row, col) = code.point(cursor);
205
206 let indent_level = code.indentation_level(row, col);
208 let indent_text = code.indent().repeat(indent_level);
209
210 let text_to_insert = format!("\n{}", indent_text);
212
213 let mut insert_action = InsertText { text: text_to_insert };
215 insert_action.apply(editor);
216 }
217}
218
219pub struct Delete;
225
226impl Action for Delete {
227 fn apply(&mut self, editor: &mut Editor) {
228 let mut cursor = editor.get_cursor();
230 let mut selection = editor.get_selection();
231
232 let code = editor.code_mut();
234 code.tx();
235 code.set_state_before(cursor, selection);
236
237 if let Some(sel) = &selection && !sel.is_empty() {
238 let (start, end) = sel.sorted();
240 code.remove(start, end);
241 cursor = start;
242 selection = None;
243 } else if cursor > 0 {
244 let (row, col) = code.point(cursor);
246 if code.is_only_indentation_before(row, col) {
247 let from = cursor - col;
248 code.remove(from, cursor);
249 cursor = from;
250 } else {
251 code.remove(cursor - 1, cursor);
252 cursor -= 1;
253 }
254 }
255
256 code.set_state_after(cursor, selection);
258 code.commit();
259
260 editor.set_cursor(cursor);
261 editor.set_selection(selection);
262 editor.reset_highlight_cache();
263 }
264}
265
266pub struct ToggleComment;
267
268impl Action for ToggleComment {
269 fn apply(&mut self, editor: &mut Editor) {
277 let mut cursor = editor.get_cursor();
279 let mut selection = editor.get_selection();
280 let selection_anchor = editor.selection_anchor();
281
282 let code = editor.code_mut();
284
285 code.tx();
286 code.set_state_before(cursor, selection);
287
288 let comment_text = code.comment();
289 let comment_len = comment_text.chars().count();
290
291 let lines_to_handle = if let Some(sel) = &selection && !sel.is_empty() {
293 let (start, end) = sel.sorted();
294 let (start_row, _) = code.point(start);
295 let (end_row, _) = code.point(end);
296 (start_row..=end_row).collect::<Vec<_>>()
297 } else {
298 let (row, _) = code.point(cursor);
299 vec![row]
300 };
301
302 let all_have_comment = lines_to_handle.iter().all(|&line_idx| {
304 let line_start = code.line_to_char(line_idx);
305 let line_len = code.line_len(line_idx);
306 line_start + comment_len <= line_start + line_len &&
307 code.slice(line_start, line_start + comment_len) == comment_text
308 });
309
310 let mut comments_added = 0usize;
312 let mut comments_removed = 0usize;
313
314 for &line_idx in lines_to_handle.iter().rev() {
315 let start = code.line_to_char(line_idx);
316 if all_have_comment {
317 let slice = code.slice(start, start + comment_len);
319 if slice == comment_text {
320 code.remove(start, start + comment_len);
321 comments_removed += 1;
322 }
323 } else {
324 code.insert(start, &comment_text);
326 comments_added += 1;
327 }
328 }
329
330 if let Some(sel) = &selection && !sel.is_empty() {
332 let (smin, _) = sel.sorted();
333 let mut anchor = selection_anchor;
334 let is_forward = anchor == smin;
335
336 if is_forward {
337 if !all_have_comment {
338 cursor += comment_len * comments_added;
339 anchor += comment_len;
340 } else {
341 cursor = cursor.saturating_sub(comment_len * comments_removed);
342 anchor = anchor.saturating_sub(comment_len);
343 }
344 } else {
345 if !all_have_comment {
346 cursor += comment_len;
347 anchor += comment_len * comments_added;
348 } else {
349 cursor = cursor.saturating_sub(comment_len);
350 anchor = anchor.saturating_sub(comment_len * comments_removed);
351 }
352 }
353
354 selection = Some(Selection::from_anchor_and_cursor(anchor, cursor));
355 } else {
356 if !all_have_comment {
357 cursor += comment_len;
358 } else {
359 cursor = cursor.saturating_sub(comment_len);
360 }
361 }
362
363 code.set_state_after(cursor, selection);
365 code.commit();
366
367 editor.set_cursor(cursor);
369 editor.set_selection(selection);
370 editor.reset_highlight_cache();
371 }
372}
373
374pub struct Indent;
380
381impl Action for Indent {
382 fn apply(&mut self, editor: &mut Editor) {
383 let mut cursor = editor.get_cursor();
385 let mut selection = editor.get_selection();
386 let selection_anchor = editor.selection_anchor();
387
388 let code = editor.code_mut();
390 code.tx();
391 code.set_state_before(cursor, selection);
392
393 let indent_text = code.indent();
394
395 let lines_to_handle = if let Some(sel) = &selection && !sel.is_empty() {
397 let (start, end) = sel.sorted();
398 let (start_row, _) = code.point(start);
399 let (end_row, _) = code.point(end);
400 (start_row..=end_row).collect::<Vec<_>>()
401 } else {
402 let (row, _) = code.point(cursor);
403 vec![row]
404 };
405
406 let mut indents_added = 0;
408 for &line_idx in lines_to_handle.iter().rev() {
409 let line_start = code.line_to_char(line_idx);
410 code.insert(line_start, &indent_text);
411 indents_added += 1;
412 }
413
414 if let Some(sel) = &selection && !sel.is_empty() {
416 let (smin, _) = sel.sorted();
417 let mut anchor = selection_anchor;
418 let is_forward = anchor == smin;
419
420 if is_forward {
421 cursor += indent_text.len() * indents_added;
422 anchor += indent_text.len();
423 } else {
424 cursor += indent_text.len();
425 anchor += indent_text.len() * indents_added;
426 }
427
428 selection = Some(Selection::from_anchor_and_cursor(anchor, cursor));
429 } else {
430 cursor += indent_text.len();
431 }
432
433 code.set_state_after(cursor, selection);
435 code.commit();
436
437 editor.set_cursor(cursor);
438 editor.set_selection(selection);
439 editor.reset_highlight_cache();
440 }
441}
442
443
444pub struct UnIndent;
450
451impl Action for UnIndent {
452 fn apply(&mut self, editor: &mut Editor) {
453 let mut cursor = editor.get_cursor();
455 let mut selection = editor.get_selection();
456 let selection_anchor = editor.selection_anchor();
457
458 let code = editor.code_mut();
460 code.tx();
461 code.set_state_before(cursor, selection);
462
463 let indent_text = code.indent();
464 let indent_len = indent_text.chars().count();
465
466 let lines_to_handle = if let Some(sel) = &selection && !sel.is_empty() {
468 let (start, end) = sel.sorted();
469 let (start_row, _) = code.point(start);
470 let (end_row, _) = code.point(end);
471 (start_row..=end_row).collect::<Vec<_>>()
472 } else {
473 let (row, _) = code.point(cursor);
474 vec![row]
475 };
476
477 let mut lines_untabbed = 0;
479 for &line_idx in lines_to_handle.iter().rev() {
480 if let Some(indent_cols) = code.find_indent_at_line_start(line_idx) {
481 let remove_count = indent_cols.min(indent_len);
482 if remove_count > 0 {
483 let line_start = code.line_to_char(line_idx);
484 code.remove(line_start, line_start + remove_count);
485 lines_untabbed += 1;
486 }
487 }
488 }
489
490 if let Some(sel) = &selection && !sel.is_empty() {
492 let (smin, _) = sel.sorted();
493 let mut anchor = selection_anchor;
494 let is_forward = anchor == smin;
495
496 if is_forward {
497 cursor = cursor.saturating_sub(indent_len * lines_untabbed);
498 anchor = anchor.saturating_sub(indent_len);
499 } else {
500 cursor = cursor.saturating_sub(indent_len);
501 anchor = anchor.saturating_sub(indent_len * lines_untabbed);
502 }
503
504 selection = Some(Selection::from_anchor_and_cursor(anchor, cursor));
505 } else {
506 cursor = cursor.saturating_sub(indent_len * lines_untabbed);
507 }
508
509 code.set_state_after(cursor, selection);
511 code.commit();
512
513 editor.set_cursor(cursor);
514 editor.set_selection(selection);
515 editor.reset_highlight_cache();
516 }
517}
518
519pub struct SelectAll;
521
522impl Action for SelectAll {
523 fn apply(&mut self, editor: &mut Editor) {
524 let from = 0;
526 let code = editor.code_mut();
527 let to = code.len_chars();
528 let sel = Selection::new(from, to);
529 editor.set_selection(Some(sel));
530 }
531}
532
533pub struct Duplicate;
539
540impl Action for Duplicate {
541 fn apply(&mut self, editor: &mut Editor) {
542 let mut cursor = editor.get_cursor();
544 let mut selection = editor.get_selection();
545 let code = editor.code_mut();
546
547 code.tx();
548 code.set_state_before(cursor, selection);
549
550 if let Some(sel) = &selection {
551 let text = code.slice(sel.start, sel.end);
553 let insert_pos = sel.end;
554 code.insert(insert_pos, &text);
555 cursor = insert_pos + text.chars().count();
556 selection = None;
557 } else {
558 let (line_start, line_end) = code.line_boundaries(cursor);
560 let line_text = code.slice(line_start, line_end);
561 let column = cursor - line_start;
562
563 let insert_pos = line_end;
564 let to_insert = if line_text.ends_with('\n') {
565 line_text.clone()
566 } else {
567 format!("{}\n", line_text)
568 };
569 code.insert(insert_pos, &to_insert);
570
571 let new_line_len = to_insert.trim_end_matches('\n').chars().count();
573 let new_column = column.min(new_line_len);
574 cursor = insert_pos + new_column;
575 }
576
577 code.set_state_after(cursor, selection);
578 code.commit();
579
580 editor.set_cursor(cursor);
582 editor.set_selection(selection);
583 editor.reset_highlight_cache();
584 }
585}
586
587pub struct DeleteLine;
589
590impl Action for DeleteLine {
591 fn apply(&mut self, editor: &mut Editor) {
592 let mut cursor = editor.get_cursor();
594 let mut selection = editor.get_selection();
595 let code = editor.code_mut();
596
597 let (start, end) = code.line_boundaries(cursor);
599
600 if start == end && start == code.len() {
602 return;
603 }
604
605 code.tx();
607 code.set_state_before(cursor, selection);
608 code.remove(start, end);
609 code.set_state_after(start, None);
610 code.commit();
611
612 cursor = start;
614 selection = None;
615 editor.set_cursor(cursor);
616 editor.set_selection(selection);
617 editor.reset_highlight_cache();
618 }
619}
620
621pub struct Cut;
623
624impl Action for Cut {
625 fn apply(&mut self, editor: &mut Editor) {
626 let mut cursor = editor.get_cursor();
628 let mut selection = editor.get_selection();
629
630 let sel = match &selection {
631 Some(sel) if !sel.is_empty() => sel.clone(),
632 _ => return, };
634
635 let text = editor.code_ref().slice(sel.start, sel.end);
637 let _ = editor.set_clipboard(&text);
638
639 let code = editor.code_mut();
641 code.tx();
642 code.set_state_before(cursor, selection);
643 code.remove(sel.start, sel.end);
644 code.set_state_after(sel.start, None);
645 code.commit();
646
647 cursor = sel.start;
649 selection = None;
650 editor.set_cursor(cursor);
651 editor.set_selection(selection);
652 editor.reset_highlight_cache();
653 }
654}
655
656pub struct Copy;
660
661impl Action for Copy {
662 fn apply(&mut self, editor: &mut Editor) {
663 let selection = editor.get_selection();
665
666 let Some(sel) = selection else { return };
668 if sel.is_empty() { return }
669
670 let text = editor.code_ref().slice(sel.start, sel.end);
672 let _ = editor.set_clipboard(&text);
673 }
674}
675
676pub struct Paste;
681
682impl Action for Paste {
683 fn apply(&mut self, editor: &mut Editor) {
684 let Ok(text) = editor.get_clipboard() else { return };
686 if text.is_empty() { return }
687
688 let mut cursor = editor.get_cursor();
690 let mut selection = editor.get_selection();
691 let code = editor.code_mut();
692
693 code.tx();
695 code.set_state_before(cursor, selection);
696
697 if let Some(sel) = &selection {
699 if !sel.is_empty() {
700 let (start, end) = sel.sorted();
701 code.remove(start, end);
702 cursor = start;
703 selection = None;
704 }
705 }
706
707 let inserted = code.smart_paste(cursor, &text);
709 cursor += inserted;
710
711 code.set_state_after(cursor, selection);
713 code.commit();
714
715 editor.set_cursor(cursor);
717 editor.set_selection(selection);
718 editor.reset_highlight_cache();
719 }
720}
721
722pub struct Undo;
727
728impl Action for Undo {
729 fn apply(&mut self, editor: &mut Editor) {
730 let code = editor.code_mut();
732
733 let edits = code.undo();
735 editor.reset_highlight_cache();
736
737 let Some(batch) = edits else { return };
739
740 if let Some(before) = batch.state_before {
742 editor.set_cursor(before.offset);
743 editor.set_selection(before.selection);
744 return;
745 }
746
747 for edit in batch.edits.iter().rev() {
749 match &edit.kind {
750 EditKind::Insert { offset, .. } => {
751 editor.set_cursor(*offset);
752 }
753 EditKind::Remove { offset, text } => {
754 editor.set_cursor(*offset + text.chars().count());
755 }
756 }
757 }
758 }
759}
760
761pub struct Redo;
766
767impl Action for Redo {
768 fn apply(&mut self, editor: &mut Editor) {
769 let code = editor.code_mut();
771
772 let edits = code.redo();
774 editor.reset_highlight_cache();
775
776 let Some(batch) = edits else { return };
778
779 if let Some(after) = batch.state_after {
781 editor.set_cursor(after.offset);
782 editor.set_selection(after.selection);
783 return;
784 }
785
786 for edit in batch.edits {
788 match &edit.kind {
789 EditKind::Insert { offset, text } => {
790 editor.set_cursor(*offset + text.chars().count());
791 }
792 EditKind::Remove { offset, .. } => {
793 editor.set_cursor(*offset);
794 }
795 }
796 }
797 }
798}