1use std::{collections::HashSet, iter, ops::Range};
2
3use itertools::Itertools;
4use lapce_xi_rope::{DeltaElement, Rope, RopeDelta};
5
6use crate::{
7 buffer::{rope_text::RopeText, Buffer, InvalLines},
8 command::EditCommand,
9 cursor::{get_first_selection_after, Cursor, CursorAffinity, CursorMode},
10 mode::{Mode, MotionMode, VisualMode},
11 register::{Clipboard, Register, RegisterData, RegisterKind},
12 selection::{InsertDrift, SelRegion, Selection},
13 util::{
14 has_unmatched_pair, matching_char, matching_pair_direction, str_is_pair_left,
15 str_matching_pair,
16 },
17 word::{get_char_property, CharClassification},
18};
19
20fn format_start_end(
21 buffer: &Buffer,
22 range: Range<usize>,
23 is_vertical: bool,
24 first_non_blank: bool,
25 count: usize,
26) -> Range<usize> {
27 let start = range.start;
28 let end = range.end;
29
30 if is_vertical {
31 let start_line = buffer.line_of_offset(start.min(end));
32 let end_line = buffer.line_of_offset(end.max(start));
33 let start = if first_non_blank {
34 buffer.first_non_blank_character_on_line(start_line)
35 } else {
36 buffer.offset_of_line(start_line)
37 };
38 let end = buffer.offset_of_line(end_line + count);
39 start..end
40 } else {
41 let s = start.min(end);
42 let e = start.max(end);
43 s..e
44 }
45}
46
47#[derive(Clone, Copy, Debug, PartialEq, Eq)]
48pub enum EditType {
49 InsertChars,
50 Delete,
51 DeleteSelection,
52 InsertNewline,
53 Cut,
54 Paste,
55 Indent,
56 Outdent,
57 ToggleComment,
58 MoveLine,
59 Completion,
60 DeleteWord,
61 DeleteToBeginningOfLine,
62 DeleteToEndOfLine,
63 DeleteToEndOfLineAndInsert,
64 MotionDelete,
65 NormalizeLineEndings,
66 Undo,
67 Redo,
68 Other,
69}
70
71impl EditType {
72 pub fn breaks_undo_group(self, previous: EditType) -> bool {
74 !((self == EditType::InsertChars || self == EditType::Delete) && self == previous)
75 }
76}
77
78pub struct EditConf<'a> {
79 pub comment_token: &'a str,
80 pub modal: bool,
81 pub smart_tab: bool,
82 pub keep_indent: bool,
83 pub auto_indent: bool,
84}
85
86pub struct Action {}
87
88impl Action {
89 pub fn insert(
90 cursor: &mut Cursor,
91 buffer: &mut Buffer,
92 s: &str,
93 prev_unmatched: &dyn Fn(&Buffer, char, usize) -> Option<usize>,
94 auto_closing_matching_pairs: bool,
95 auto_surround: bool,
96 ) -> Vec<(Rope, RopeDelta, InvalLines)> {
97 let mut deltas = Vec::new();
98 if let CursorMode::Insert(selection) = &cursor.mode {
99 if s.chars().count() != 1 {
100 let (text, delta, inval_lines) =
101 buffer.edit([(selection, s)], EditType::InsertChars);
102 let selection = selection.apply_delta(&delta, true, InsertDrift::Default);
103 deltas.push((text, delta, inval_lines));
104 cursor.mode = CursorMode::Insert(selection);
105 } else {
106 let c = s.chars().next().unwrap();
107 let matching_pair_type = matching_pair_direction(c);
108
109 let mut edits = vec![];
111
112 let mut edits_after = vec![];
114
115 let mut selection = selection.clone();
116 for (idx, region) in selection.regions_mut().iter_mut().enumerate() {
117 let offset = region.end;
118 let cursor_char = buffer.char_at_offset(offset);
119 let prev_offset = buffer.move_left(offset, Mode::Normal, 1);
120 let prev_cursor_char = if prev_offset < offset {
121 buffer.char_at_offset(prev_offset)
122 } else {
123 None
124 };
125
126 if region.start != region.end
129 && auto_surround
130 && (matching_pair_type == Some(true) || c == '"' || c == '\'' || c == '`')
131 {
132 edits.push((Selection::imaginary_caret(region.min()), c.to_string()));
133 edits_after.push((
134 idx,
135 match c {
136 '"' => '"',
137 '\'' => '\'',
138 '`' => '`',
139 _ => matching_char(c).unwrap(),
140 },
141 ));
142 continue;
143 }
144
145 if auto_closing_matching_pairs {
146 if (c == '"' || c == '\'' || c == '`') && cursor_char == Some(c) {
147 let new_offset = buffer.next_grapheme_offset(offset, 1, buffer.len());
149
150 *region = SelRegion::caret(new_offset, CursorAffinity::Backward);
151 continue;
152 }
153
154 if matching_pair_type == Some(false) {
155 if cursor_char == Some(c) {
156 let new_offset =
158 buffer.next_grapheme_offset(offset, 1, buffer.len());
159
160 *region = SelRegion::caret(new_offset, CursorAffinity::Backward);
161 continue;
162 }
163
164 let line = buffer.line_of_offset(offset);
165 let line_start = buffer.offset_of_line(line);
166 if buffer.slice_to_cow(line_start..offset).trim() == "" {
167 let opening_character = matching_char(c).unwrap();
168 if let Some(previous_offset) =
169 prev_unmatched(buffer, opening_character, offset)
170 {
171 let previous_line = buffer.line_of_offset(previous_offset);
173 let line_indent = buffer.indent_on_line(previous_line);
174
175 let current_selection = Selection::region(
176 line_start,
177 offset,
178 CursorAffinity::Backward,
179 );
180
181 edits.push((current_selection, format!("{line_indent}{c}")));
182 continue;
183 }
184 }
185 }
186
187 if matching_pair_type == Some(true) || c == '"' || c == '\'' || c == '`' {
188 let is_whitespace_or_punct = cursor_char
190 .map(|c| {
191 let prop = get_char_property(c);
192 prop == CharClassification::Lf
193 || prop == CharClassification::Cr
194 || prop == CharClassification::Space
195 || prop == CharClassification::Punctuation
196 })
197 .unwrap_or(true);
198
199 let should_insert_pair = match c {
200 '"' | '\'' | '`' => {
201 is_whitespace_or_punct
202 && prev_cursor_char
203 .map(|c| {
204 let prop = get_char_property(c);
205 prop == CharClassification::Lf
206 || prop == CharClassification::Cr
207 || prop == CharClassification::Space
208 || prop == CharClassification::Punctuation
209 })
210 .unwrap_or(true)
211 }
212 _ => is_whitespace_or_punct,
213 };
214
215 if should_insert_pair {
216 let insert_after = match c {
217 '"' => '"',
218 '\'' => '\'',
219 '`' => '`',
220 _ => matching_char(c).unwrap(),
221 };
222 edits_after.push((idx, insert_after));
223 }
224 };
225 }
226
227 let current_selection = Selection::imaginary_region(region.start, region.end);
228
229 edits.push((current_selection, c.to_string()));
230 }
231
232 let edits = edits
234 .iter()
235 .map(|(selection, content)| (selection, content.as_str()))
236 .collect::<Vec<_>>();
237
238 let (text, delta, inval_lines) = buffer.edit(&edits, EditType::InsertChars);
239
240 buffer.set_cursor_before(CursorMode::Insert(selection.clone()));
241
242 let mut selection = selection.apply_delta(&delta, true, InsertDrift::Default);
244
245 buffer.set_cursor_after(CursorMode::Insert(selection.clone()));
246
247 deltas.push((text, delta, inval_lines));
248 let edits_after = edits_after
250 .iter()
251 .map(|(idx, content)| {
252 let region = &selection.regions()[*idx];
253 (
254 Selection::imaginary_caret(region.max()),
255 content.to_string(),
256 )
257 })
258 .collect::<Vec<_>>();
259
260 let edits_after = edits_after
261 .iter()
262 .map(|(selection, content)| (selection, content.as_str()))
263 .collect::<Vec<_>>();
264
265 if !edits_after.is_empty() {
266 let (text, delta, inval_lines) =
267 buffer.edit(&edits_after, EditType::InsertChars);
268 deltas.push((text, delta, inval_lines));
269 }
270
271 let mut adjustment = 0;
273 for region in selection
274 .regions_mut()
275 .iter_mut()
276 .sorted_by(|region_a, region_b| region_a.start.cmp(®ion_b.start))
277 {
278 let new_region = SelRegion::new(
279 region.start + adjustment,
280 region.end + adjustment,
281 CursorAffinity::Backward,
282 None,
283 );
284
285 if let Some(inserted) = edits_after.iter().find_map(|(selection, str)| {
286 if selection.last_inserted().map(|r| r.start) == Some(region.start) {
287 Some(str)
288 } else {
289 None
290 }
291 }) {
292 adjustment += inserted.len();
293 }
294
295 *region = new_region;
296 }
297
298 cursor.mode = CursorMode::Insert(selection);
299 }
300 }
301 deltas
302 }
303
304 fn toggle_visual(cursor: &mut Cursor, visual_mode: VisualMode, modal: bool) {
305 if !modal {
306 return;
307 }
308
309 match &cursor.mode {
310 CursorMode::Visual {
311 start,
312 end,
313 mode,
314 affinity,
315 } => {
316 if mode != &visual_mode {
317 cursor.mode = CursorMode::Visual {
318 start: *start,
319 end: *end,
320 mode: visual_mode,
321 affinity: *affinity,
322 };
323 } else {
324 let affinity = cursor.affinity();
325 cursor.mode = CursorMode::Normal {
326 offset: *end,
327 affinity,
328 };
329 };
330 }
331 _ => {
332 let offset = cursor.offset();
333 let affinity = cursor.affinity();
334 cursor.mode = CursorMode::Visual {
335 start: offset,
336 end: offset,
337 mode: visual_mode,
338 affinity,
339 };
340 }
341 }
342 }
343
344 fn insert_new_line(
345 buffer: &mut Buffer,
346 cursor: &mut Cursor,
347 selection: Selection,
348 keep_indent: bool,
349 auto_indent: bool,
350 ) -> Vec<(Rope, RopeDelta, InvalLines)> {
351 let mut edits = Vec::with_capacity(selection.regions().len());
352 let mut extra_edits = Vec::new();
353 let mut shift = 0i32;
354 let line_ending = buffer.line_ending().get_chars();
355 for region in selection.regions() {
356 let offset = region.max();
357 let line = buffer.line_of_offset(offset);
358 let line_start = buffer.offset_of_line(line);
359 let line_end = buffer.line_end_offset(line, true);
360 let line_indent = buffer.indent_on_line(line);
361 let first_half = buffer.slice_to_cow(line_start..offset);
362 let second_half = buffer.slice_to_cow(offset..line_end);
363 let second_half_trim = second_half.trim();
364
365 let new_line_content = {
367 let indent_storage;
368 let indent = if auto_indent && has_unmatched_pair(&first_half) {
369 indent_storage = format!("{}{}", line_indent, buffer.indent_unit());
370 &indent_storage
371 } else if keep_indent
372 && (second_half_trim.is_empty() || !second_half.starts_with(&line_indent))
373 {
374 &line_indent
375 } else {
376 indent_storage = String::new();
377 &indent_storage
378 };
379 format!("{line_ending}{indent}")
380 };
381
382 let selection = Selection::imaginary_region(region.min(), region.max());
383
384 shift -= (region.max() - region.min()) as i32;
385 shift += new_line_content.len() as i32;
386
387 edits.push((selection, new_line_content));
388
389 if let Some(c) = first_half.chars().rev().find(|&c| c != ' ') {
390 if let Some(true) = matching_pair_direction(c) {
391 if let Some(c) = matching_char(c) {
392 if second_half_trim.starts_with(c) {
393 let selection =
394 Selection::imaginary_caret((region.max() as i32 + shift) as usize);
395 let content = format!("{line_ending}{line_indent}",);
396 extra_edits.push((selection, content));
397 }
398 }
399 }
400 }
401 }
402
403 let edits = edits
404 .iter()
405 .map(|(selection, s)| (selection, s.as_str()))
406 .collect::<Vec<_>>();
407 let (text, delta, inval_lines) = buffer.edit(&edits, EditType::InsertNewline);
408 let mut selection = selection.apply_delta(&delta, true, InsertDrift::Default);
409
410 let mut deltas = vec![(text, delta, inval_lines)];
411
412 if !extra_edits.is_empty() {
413 let edits = extra_edits
414 .iter()
415 .map(|(selection, s)| (selection, s.as_str()))
416 .collect::<Vec<_>>();
417 let (text, delta, inval_lines) = buffer.edit(&edits, EditType::InsertNewline);
418 selection = selection.apply_delta(&delta, false, InsertDrift::Default);
419 deltas.push((text, delta, inval_lines));
420 }
421
422 cursor.mode = CursorMode::Insert(selection);
423
424 deltas
425 }
426
427 pub fn execute_motion_mode(
428 cursor: &mut Cursor,
429 buffer: &mut Buffer,
430 motion_mode: MotionMode,
431 range: Range<usize>,
432 is_vertical: bool,
433 register: &mut Register,
434 ) -> Vec<(Rope, RopeDelta, InvalLines)> {
435 let mut deltas = Vec::new();
436 match motion_mode {
437 MotionMode::Delete { .. } => {
438 let range = format_start_end(buffer, range, is_vertical, false, 1);
439 register.add(
440 RegisterKind::Delete,
441 RegisterData {
442 content: buffer.slice_to_cow(range.clone()).to_string(),
443 mode: if is_vertical {
444 VisualMode::Linewise
445 } else {
446 VisualMode::Normal
447 },
448 },
449 );
450 let selection = Selection::region(range.start, range.end, CursorAffinity::Forward);
451 let (text, delta, inval_lines) =
452 buffer.edit([(&selection, "")], EditType::MotionDelete);
453 cursor.apply_delta(&delta);
454 deltas.push((text, delta, inval_lines));
455 }
456 MotionMode::Yank { .. } => {
457 let range = format_start_end(buffer, range, is_vertical, false, 1);
458 register.add(
459 RegisterKind::Yank,
460 RegisterData {
461 content: buffer.slice_to_cow(range).to_string(),
462 mode: if is_vertical {
463 VisualMode::Linewise
464 } else {
465 VisualMode::Normal
466 },
467 },
468 );
469 }
470 MotionMode::Indent => {
471 let selection = Selection::region(range.start, range.end, CursorAffinity::Backward);
472 let (text, delta, inval_lines) = Self::do_indent(buffer, selection);
473 deltas.push((text, delta, inval_lines));
474 }
475 MotionMode::Outdent => {
476 let selection = Selection::region(range.start, range.end, CursorAffinity::Forward);
477 let (text, delta, inval_lines) = Self::do_outdent(buffer, selection);
478 deltas.push((text, delta, inval_lines));
479 }
480 }
481 deltas
482 }
483
484 fn compute_paste_edit(
490 buffer: &mut Buffer,
491 selection: &Selection,
492 content: &str,
493 mode: VisualMode,
494 ) -> (Rope, RopeDelta, InvalLines) {
495 if selection.len() > 1 {
496 let line_ends: Vec<_> = content.match_indices('\n').map(|(idx, _)| idx).collect();
497
498 match mode {
499 VisualMode::Normal if (line_ends.len() + 1) % selection.len() == 0 => {
503 let lines_per_group = (line_ends.len() + 1) / selection.len();
504 let mut start_idx = 0;
505 let last_line_start = line_ends
506 .len()
507 .checked_sub(lines_per_group)
508 .and_then(|line_idx| line_ends.get(line_idx))
509 .map(|line_end| line_end + 1)
510 .unwrap_or(0);
511
512 let groups = line_ends
513 .iter()
514 .skip(lines_per_group - 1)
515 .step_by(lines_per_group)
516 .map(|&end_idx| {
517 let group = &content[start_idx..end_idx];
518 let group = group.strip_suffix('\r').unwrap_or(group);
519 start_idx = end_idx + 1;
520
521 group
522 })
523 .chain(iter::once(&content[last_line_start..]));
524
525 let edits = selection
526 .regions()
527 .iter()
528 .copied()
529 .map(Selection::sel_region)
530 .zip(groups);
531
532 buffer.edit(edits, EditType::Paste)
533 }
534 VisualMode::Linewise | VisualMode::Blockwise
538 if line_ends.len() % selection.len() == 0 =>
539 {
540 let lines_per_group = line_ends.len() / selection.len();
541 let mut start_idx = 0;
542
543 let groups = line_ends
544 .iter()
545 .skip(lines_per_group - 1)
546 .step_by(lines_per_group)
547 .map(|&end_idx| {
548 let group = &content[start_idx..=end_idx];
549 start_idx = end_idx + 1;
550
551 group
552 });
553
554 let edits = selection
555 .regions()
556 .iter()
557 .copied()
558 .map(Selection::sel_region)
559 .zip(groups);
560
561 buffer.edit(edits, EditType::Paste)
562 }
563 _ => buffer.edit([(&selection, content)], EditType::Paste),
564 }
565 } else {
566 buffer.edit([(&selection, content)], EditType::Paste)
567 }
568 }
569
570 pub fn do_paste(
571 cursor: &mut Cursor,
572 buffer: &mut Buffer,
573 data: &RegisterData,
574 ) -> Vec<(Rope, RopeDelta, InvalLines)> {
575 let mut deltas = Vec::new();
576 match data.mode {
577 VisualMode::Normal => {
578 let selection = match cursor.mode {
579 CursorMode::Normal { offset, affinity } => {
580 let line_end = buffer.offset_line_end(offset, true);
581 let offset = (offset + 1).min(line_end);
582 Selection::caret(offset, affinity)
583 }
584 CursorMode::Insert { .. } | CursorMode::Visual { .. } => {
585 cursor.edit_selection(buffer)
586 }
587 };
588 let after = cursor.is_insert() || !data.content.contains('\n');
589 let (text, delta, inval_lines) =
590 Self::compute_paste_edit(buffer, &selection, &data.content, data.mode);
591 let selection = selection.apply_delta(&delta, after, InsertDrift::Default);
592 deltas.push((text, delta, inval_lines));
593 if !after {
594 cursor.update_selection(buffer, selection);
595 } else {
596 match cursor.mode {
597 CursorMode::Normal { .. } | CursorMode::Visual { .. } => {
598 let offset = buffer.prev_grapheme_offset(selection.min_offset(), 1, 0);
599 cursor.mode = CursorMode::Normal {
600 offset,
601 affinity: selection.get_cursor_affinity(),
602 };
603 }
604 CursorMode::Insert { .. } => {
605 cursor.mode = CursorMode::Insert(selection);
606 }
607 }
608 }
609 }
610 VisualMode::Linewise | VisualMode::Blockwise => {
611 let (selection, content) = match &cursor.mode {
612 CursorMode::Normal { offset, .. } => {
613 let line = buffer.line_of_offset(*offset);
614 let offset = buffer.offset_of_line(line + 1);
615 (
616 Selection::caret(offset, CursorAffinity::Backward),
617 data.content.clone(),
618 )
619 }
620 CursorMode::Insert(selection) => {
621 let mut selection = selection.clone();
622 for region in selection.regions_mut() {
623 if region.is_caret() {
624 let line = buffer.line_of_offset(region.start);
625 let start = buffer.offset_of_line(line);
626 region.start = start;
627 region.end = start;
628 }
629 }
630 (selection, data.content.clone())
631 }
632 CursorMode::Visual { mode, .. } => {
633 let selection = cursor.edit_selection(buffer);
634 let data = match mode {
635 VisualMode::Linewise => data.content.clone(),
636 _ => "\n".to_string() + &data.content,
637 };
638 (selection, data)
639 }
640 };
641 let (text, delta, inval_lines) =
642 Self::compute_paste_edit(buffer, &selection, &content, data.mode);
643 let selection =
644 selection.apply_delta(&delta, cursor.is_insert(), InsertDrift::Default);
645 deltas.push((text, delta, inval_lines));
646 match cursor.mode {
647 CursorMode::Normal { .. } | CursorMode::Visual { .. } => {
648 let offset = selection.min_offset();
649 let offset = if cursor.is_visual() {
650 offset + 1
651 } else {
652 offset
653 };
654 let line = buffer.line_of_offset(offset);
655 let offset = buffer.first_non_blank_character_on_line(line);
656 cursor.mode = CursorMode::Normal {
657 offset,
658 affinity: selection.get_cursor_affinity(),
659 };
660 }
661 CursorMode::Insert(_) => {
662 cursor.mode = CursorMode::Insert(selection);
663 }
664 }
665 }
666 }
667 deltas
668 }
669
670 fn do_indent(buffer: &mut Buffer, selection: Selection) -> (Rope, RopeDelta, InvalLines) {
671 let indent = buffer.indent_unit();
672 let mut edits = Vec::new();
673
674 let mut lines = HashSet::new();
675 for region in selection.regions() {
676 let start_line = buffer.line_of_offset(region.min());
677 let mut end_line = buffer.line_of_offset(region.max());
678 if end_line > start_line {
679 let end_line_start = buffer.offset_of_line(end_line);
680 if end_line_start == region.max() {
681 end_line -= 1;
682 }
683 }
684 for line in start_line..=end_line {
685 if lines.insert(line) {
686 let line_content = buffer.line_content(line);
687 if line_content == "\n" || line_content == "\r\n" {
688 continue;
689 }
690 let nonblank = buffer.first_non_blank_character_on_line(line);
691 let edit = crate::indent::create_edit(buffer, nonblank, indent);
692 edits.push(edit);
693 }
694 }
695 }
696
697 buffer.edit(&edits, EditType::Indent)
698 }
699
700 fn do_outdent(buffer: &mut Buffer, selection: Selection) -> (Rope, RopeDelta, InvalLines) {
701 let indent = buffer.indent_unit();
702 let mut edits = Vec::new();
703
704 let mut lines = HashSet::new();
705 for region in selection.regions() {
706 let start_line = buffer.line_of_offset(region.min());
707 let mut end_line = buffer.line_of_offset(region.max());
708 if end_line > start_line {
709 let end_line_start = buffer.offset_of_line(end_line);
710 if end_line_start == region.max() {
711 end_line -= 1;
712 }
713 }
714 for line in start_line..=end_line {
715 if lines.insert(line) {
716 let line_content = buffer.line_content(line);
717 if line_content == "\n" || line_content == "\r\n" {
718 continue;
719 }
720 let nonblank = buffer.first_non_blank_character_on_line(line);
721 if let Some(edit) = crate::indent::create_outdent(buffer, nonblank, indent) {
722 edits.push(edit);
723 }
724 }
725 }
726 }
727
728 buffer.edit(&edits, EditType::Outdent)
729 }
730
731 fn duplicate_line(
732 cursor: &mut Cursor,
733 buffer: &mut Buffer,
734 direction: DuplicateDirection,
735 ) -> Vec<(Rope, RopeDelta, InvalLines)> {
736 let selection = match cursor.mode {
738 CursorMode::Insert(ref mut sel) => sel,
739 _ => return vec![],
740 };
741
742 let mut line_ranges = HashSet::new();
743 for region in selection.regions_mut() {
744 let start_line = buffer.line_of_offset(region.start);
745 let end_line = buffer.line_of_offset(region.end) + 1;
746
747 line_ranges.insert(start_line..end_line);
748 }
749
750 let mut edits = vec![];
751 for range in line_ranges {
752 let start = buffer.offset_of_line(range.start);
753 let end = buffer.offset_of_line(range.end);
754
755 let content = buffer.slice_to_cow(start..end).into_owned();
756 edits.push((
757 match direction {
758 DuplicateDirection::Up => Selection::caret(end, CursorAffinity::Backward),
759 DuplicateDirection::Down => Selection::caret(start, CursorAffinity::Backward),
760 },
761 content,
762 ));
763 }
764
765 let edits = edits
766 .iter()
767 .map(|(sel, content)| (sel, content.as_str()))
768 .collect::<Vec<_>>();
769
770 let (text, delta, inval_lines) = buffer.edit(&edits, EditType::InsertChars);
771
772 *selection = selection.apply_delta(&delta, true, InsertDrift::Default);
773
774 vec![(text, delta, inval_lines)]
775 }
776
777 #[allow(clippy::too_many_arguments)]
778 pub fn do_edit<T: Clipboard>(
779 cursor: &mut Cursor,
780 buffer: &mut Buffer,
781 cmd: &EditCommand,
782 clipboard: &mut T,
783 register: &mut Register,
784 EditConf {
785 comment_token,
786 modal,
787 smart_tab,
788 keep_indent,
789 auto_indent,
790 }: EditConf,
791 ) -> Vec<(Rope, RopeDelta, InvalLines)> {
792 use crate::command::EditCommand::*;
793 match cmd {
794 MoveLineUp => {
795 let mut deltas = Vec::new();
796 if let CursorMode::Insert(mut selection) = cursor.mode.clone() {
797 for region in selection.regions_mut() {
798 let start_line = buffer.line_of_offset(region.min());
799 if start_line > 0 {
800 let previous_line_len = buffer.line_content(start_line - 1).len();
801
802 let end_line = buffer.line_of_offset(region.max());
803 let start = buffer.offset_of_line(start_line);
804 let end = buffer.offset_of_line(end_line + 1);
805 let content = buffer.slice_to_cow(start..end).to_string();
806 let (text, delta, inval_lines) = buffer.edit(
807 [
808 (&Selection::region(start, end, region.affinity), ""),
809 (
810 &Selection::caret(
811 buffer.offset_of_line(start_line - 1),
812 region.affinity,
813 ),
814 &content,
815 ),
816 ],
817 EditType::MoveLine,
818 );
819 deltas.push((text, delta, inval_lines));
820 region.start -= previous_line_len;
821 region.end -= previous_line_len;
822 }
823 }
824 cursor.mode = CursorMode::Insert(selection);
825 }
826 deltas
827 }
828 MoveLineDown => {
829 let mut deltas = Vec::new();
830 if let CursorMode::Insert(mut selection) = cursor.mode.clone() {
831 for region in selection.regions_mut().iter_mut().rev() {
832 let last_line = buffer.last_line();
833 let start_line = buffer.line_of_offset(region.min());
834 let end_line = buffer.line_of_offset(region.max());
835 if end_line < last_line {
836 let next_line_len = buffer.line_content(end_line + 1).len();
837
838 let start = buffer.offset_of_line(start_line);
839 let end = buffer.offset_of_line(end_line + 1);
840 let content = buffer.slice_to_cow(start..end).to_string();
841 let (text, delta, inval_lines) = buffer.edit(
842 [
843 (
844 &Selection::caret(
845 buffer.offset_of_line(end_line + 2),
846 region.affinity,
847 ),
848 content.as_str(),
849 ),
850 (&Selection::region(start, end, region.affinity), ""),
851 ],
852 EditType::MoveLine,
853 );
854 deltas.push((text, delta, inval_lines));
855 region.start += next_line_len;
856 region.end += next_line_len;
857 }
858 }
859 cursor.mode = CursorMode::Insert(selection);
860 }
861 deltas
862 }
863 InsertNewLine => match cursor.mode.clone() {
864 CursorMode::Normal { offset, .. } => Self::insert_new_line(
865 buffer,
866 cursor,
867 Selection::imaginary_caret(offset),
868 keep_indent,
869 auto_indent,
870 ),
871 CursorMode::Insert(selection) => {
872 Self::insert_new_line(buffer, cursor, selection, keep_indent, auto_indent)
873 }
874 CursorMode::Visual { .. } => {
875 vec![]
876 }
877 },
878 InsertTab => {
879 let mut deltas = Vec::new();
880 if let CursorMode::Insert(selection) = &cursor.mode {
881 if smart_tab {
882 let indent = buffer.indent_unit();
883 let mut edits = Vec::new();
884
885 for region in selection.regions() {
886 if region.is_caret() {
887 edits.push(crate::indent::create_edit(buffer, region.start, indent))
888 } else {
889 let start_line = buffer.line_of_offset(region.min());
890 let end_line = buffer.line_of_offset(region.max());
891 for line in start_line..=end_line {
892 let offset = buffer.first_non_blank_character_on_line(line);
893 edits.push(crate::indent::create_edit(buffer, offset, indent))
894 }
895 }
896 }
897
898 let (text, delta, inval_lines) = buffer.edit(&edits, EditType::InsertChars);
899 let selection = selection.apply_delta(&delta, true, InsertDrift::Default);
900 deltas.push((text, delta, inval_lines));
901 cursor.mode = CursorMode::Insert(selection);
902 } else {
903 let (text, delta, inval_lines) =
904 buffer.edit([(&selection, "\t")], EditType::InsertChars);
905 let selection = selection.apply_delta(&delta, true, InsertDrift::Default);
906 deltas.push((text, delta, inval_lines));
907 cursor.mode = CursorMode::Insert(selection);
908 }
909 }
910 deltas
911 }
912 IndentLine => {
913 let selection = cursor.edit_selection(buffer);
914 let (text, delta, inval_lines) = Self::do_indent(buffer, selection);
915 cursor.apply_delta(&delta);
916 vec![(text, delta, inval_lines)]
917 }
918 JoinLines => {
919 let offset = cursor.offset();
920 let (line, _col) = buffer.offset_to_line_col(offset);
921 if line < buffer.last_line() {
922 let start = buffer.line_end_offset(line, true);
923 let end = buffer.first_non_blank_character_on_line(line + 1);
924 vec![buffer.edit(
925 [(&Selection::imaginary_region(start, end), " ")],
926 EditType::Other,
927 )]
928 } else {
929 vec![]
930 }
931 }
932 OutdentLine => {
933 let selection = cursor.edit_selection(buffer);
934 let (text, delta, inval_lines) = Self::do_outdent(buffer, selection);
935 cursor.apply_delta(&delta);
936 vec![(text, delta, inval_lines)]
937 }
938 ToggleLineComment => {
939 let mut lines = HashSet::new();
940 let selection = cursor.edit_selection(buffer);
941 let mut had_comment = true;
942 let mut smallest_indent = usize::MAX;
943 for region in selection.regions() {
944 let mut line = buffer.line_of_offset(region.min());
945 let end_line = buffer.line_of_offset(region.max());
946 let end_line_offset = buffer.offset_of_line(end_line);
947 let end = if end_line > line && region.max() == end_line_offset {
948 end_line_offset
949 } else {
950 buffer.offset_of_line(end_line + 1)
951 };
952 let start = buffer.offset_of_line(line);
953 for content in buffer.text().lines(start..end) {
954 let trimmed_content = content.trim_start();
955 if trimmed_content.is_empty() {
956 line += 1;
957 continue;
958 }
959 let indent = content.len() - trimmed_content.len();
960 if indent < smallest_indent {
961 smallest_indent = indent;
962 }
963 if !trimmed_content.starts_with(comment_token) {
964 had_comment = false;
965 lines.insert((line, indent, 0));
966 } else {
967 let had_space_after_comment =
968 trimmed_content.chars().nth(comment_token.len()) == Some(' ');
969 lines.insert((
970 line,
971 indent,
972 comment_token.len() + usize::from(had_space_after_comment),
973 ));
974 }
975 line += 1;
976 }
977 }
978
979 let (text, delta, inval_lines) = if had_comment {
980 let mut selection = Selection::new();
981 for (line, indent, len) in lines.iter() {
982 let start = buffer.offset_of_line(*line) + indent;
983 selection.add_region(SelRegion::imaginary_region(start, start + len))
984 }
985 buffer.edit([(&selection, "")], EditType::ToggleComment)
986 } else {
987 let mut selection = Selection::new();
988 for (line, _, _) in lines.iter() {
989 let start = buffer.offset_of_line(*line) + smallest_indent;
990 selection.add_region(SelRegion::imaginary_caret(start))
991 }
992 buffer.edit(
993 [(&selection, format!("{comment_token} ").as_str())],
994 EditType::ToggleComment,
995 )
996 };
997 cursor.apply_delta(&delta);
998 vec![(text, delta, inval_lines)]
999 }
1000 Undo => {
1001 if let Some((text, delta, inval_lines, cursor_mode)) = buffer.do_undo() {
1002 apply_undo_redo(cursor, buffer, modal, text, delta, inval_lines, cursor_mode)
1003 } else {
1004 vec![]
1005 }
1006 }
1007 Redo => {
1008 if let Some((text, delta, inval_lines, cursor_mode)) = buffer.do_redo() {
1009 apply_undo_redo(cursor, buffer, modal, text, delta, inval_lines, cursor_mode)
1010 } else {
1011 vec![]
1012 }
1013 }
1014 ClipboardCopy => {
1015 let data = cursor.yank(buffer);
1016 clipboard.put_string(data.content);
1017
1018 match &cursor.mode {
1019 CursorMode::Visual { start, end, .. } => {
1020 let offset = *start.min(end);
1021 let offset = buffer.offset_line_end(offset, false).min(offset);
1022 cursor.mode = CursorMode::Normal {
1023 offset,
1024 affinity: CursorAffinity::Forward,
1025 };
1026 }
1027 CursorMode::Normal { .. } | CursorMode::Insert(_) => {}
1028 }
1029 vec![]
1030 }
1031 ClipboardCut => {
1032 let data = cursor.yank(buffer);
1033 clipboard.put_string(data.content);
1034
1035 let selection = if let CursorMode::Insert(mut selection) = cursor.mode.clone() {
1036 for region in selection.regions_mut() {
1037 if region.is_caret() {
1038 let line = buffer.line_of_offset(region.start);
1039 let start = buffer.offset_of_line(line);
1040 let end = buffer.offset_of_line(line + 1);
1041 region.start = start;
1042 region.end = end;
1043 }
1044 }
1045 selection
1046 } else {
1047 cursor.edit_selection(buffer)
1048 };
1049
1050 let (text, delta, inval_lines) = buffer.edit([(&selection, "")], EditType::Cut);
1051 let selection = selection.apply_delta(&delta, true, InsertDrift::Default);
1052 cursor.update_selection(buffer, selection);
1053 vec![(text, delta, inval_lines)]
1054 }
1055 ClipboardPaste => {
1056 if let Some(s) = clipboard.get_string() {
1057 let mode = if s.ends_with('\n') {
1058 VisualMode::Linewise
1059 } else {
1060 VisualMode::Normal
1061 };
1062 let data = RegisterData { content: s, mode };
1063 Self::do_paste(cursor, buffer, &data)
1064 } else {
1065 vec![]
1066 }
1067 }
1068 Yank => {
1069 match &cursor.mode {
1070 CursorMode::Visual { start, end, .. } => {
1071 let data = cursor.yank(buffer);
1072 register.add_yank(data);
1073
1074 let offset = *start.min(end);
1075 let offset = buffer.offset_line_end(offset, false).min(offset);
1076 cursor.mode = CursorMode::Normal {
1077 offset,
1078 affinity: CursorAffinity::Forward,
1079 };
1080 }
1081 CursorMode::Normal { .. } => {}
1082 CursorMode::Insert(_) => {}
1083 }
1084 vec![]
1085 }
1086 Paste => {
1087 let data = register.unnamed.clone();
1088 Self::do_paste(cursor, buffer, &data)
1089 }
1090 PasteBefore => {
1091 let offset = cursor.offset();
1092 let data = register.unnamed.clone();
1093 let mut local_cursor =
1094 Cursor::new(CursorMode::Insert(Selection::new()), None, None);
1095 local_cursor.set_offset(offset, CursorAffinity::Forward, false, false);
1096 Self::do_paste(&mut local_cursor, buffer, &data)
1097 }
1098 NewLineAbove => {
1099 let offset = cursor.offset();
1100 let line = buffer.line_of_offset(offset);
1101 let offset = if line > 0 {
1102 buffer.line_end_offset(line - 1, true)
1103 } else {
1104 buffer.first_non_blank_character_on_line(line)
1105 };
1106 let delta = Self::insert_new_line(
1107 buffer,
1108 cursor,
1109 Selection::imaginary_caret(offset),
1110 keep_indent,
1111 auto_indent,
1112 );
1113 if line == 0 {
1114 cursor.mode =
1115 CursorMode::Insert(Selection::caret(offset, CursorAffinity::Backward));
1116 }
1117 delta
1118 }
1119 NewLineBelow => {
1120 let offset = cursor.offset();
1121 let offset = buffer.offset_line_end(offset, true);
1122 Self::insert_new_line(
1123 buffer,
1124 cursor,
1125 Selection::imaginary_caret(offset),
1126 keep_indent,
1127 auto_indent,
1128 )
1129 }
1130 DeleteBackward => {
1131 let (selection, edit_type) = match cursor.mode {
1132 CursorMode::Normal { .. } => (cursor.edit_selection(buffer), EditType::Delete),
1133 CursorMode::Visual { .. } => {
1134 (cursor.edit_selection(buffer), EditType::DeleteSelection)
1135 }
1136 CursorMode::Insert(_) => {
1137 let selection = cursor.edit_selection(buffer);
1138 let edit_type = if selection.is_caret() {
1139 EditType::Delete
1140 } else {
1141 EditType::DeleteSelection
1142 };
1143 let indent = buffer.indent_unit();
1144 let mut new_selection = Selection::new();
1145 for region in selection.regions() {
1146 let new_region = if region.is_caret() {
1147 if indent.starts_with('\t') {
1148 let new_end = buffer.move_left(region.end, Mode::Insert, 1);
1149 SelRegion::new(region.start, new_end, region.affinity, None)
1150 } else {
1151 let line = buffer.line_of_offset(region.start);
1152 let nonblank = buffer.first_non_blank_character_on_line(line);
1153 let (_, col) = buffer.offset_to_line_col(region.start);
1154 let count = if region.start <= nonblank && col > 0 {
1155 let r = col % indent.len();
1156 if r == 0 {
1157 indent.len()
1158 } else {
1159 r
1160 }
1161 } else {
1162 1
1163 };
1164 let new_end = buffer.move_left(region.end, Mode::Insert, count);
1165 SelRegion::new(region.start, new_end, region.affinity, None)
1166 }
1167 } else {
1168 *region
1169 };
1170 new_selection.add_region(new_region);
1171 }
1172
1173 let mut selection = new_selection;
1174 if selection.regions().len() == 1 {
1175 let delete_str = buffer
1176 .slice_to_cow(selection.min_offset()..selection.max_offset())
1177 .to_string();
1178 if str_is_pair_left(&delete_str)
1179 || delete_str == "\""
1180 || delete_str == "'"
1181 || delete_str == "`"
1182 {
1183 let matching_char = match delete_str.as_str() {
1184 "\"" => Some('"'),
1185 "'" => Some('\''),
1186 "`" => Some('`'),
1187 _ => str_matching_pair(&delete_str),
1188 };
1189 if let Some(c) = matching_char {
1190 let offset = selection.max_offset();
1191 let line = buffer.line_of_offset(offset);
1192 let line_end = buffer.line_end_offset(line, true);
1193 let content = buffer.slice_to_cow(offset..line_end).to_string();
1194 if content.trim().starts_with(&c.to_string()) {
1195 let index = content.match_indices(c).next().unwrap().0;
1196 let affinity = selection.get_cursor_affinity();
1197 selection = Selection::region(
1198 selection.min_offset(),
1199 offset + index + 1,
1200 affinity,
1201 );
1202 }
1203 }
1204 }
1205 }
1206 (selection, edit_type)
1207 }
1208 };
1209 let (text, delta, inval_lines) = buffer.edit([(&selection, "")], edit_type);
1210 let selection = selection.apply_delta(&delta, true, InsertDrift::Default);
1211 cursor.update_selection(buffer, selection);
1212 vec![(text, delta, inval_lines)]
1213 }
1214 DeleteForward => {
1215 let (selection, edit_type) = match cursor.mode {
1216 CursorMode::Normal { .. } => (cursor.edit_selection(buffer), EditType::Delete),
1217 CursorMode::Visual { .. } => {
1218 (cursor.edit_selection(buffer), EditType::DeleteSelection)
1219 }
1220 CursorMode::Insert(_) => {
1221 let selection = cursor.edit_selection(buffer);
1222 let edit_type = if selection.is_caret() {
1223 EditType::Delete
1224 } else {
1225 EditType::DeleteSelection
1226 };
1227 let mut new_selection = Selection::new();
1228 for region in selection.regions() {
1229 let new_region = if region.is_caret() {
1230 let new_end = buffer.move_right(region.end, Mode::Insert, 1);
1231 SelRegion::new(region.start, new_end, region.affinity, None)
1232 } else {
1233 *region
1234 };
1235 new_selection.add_region(new_region);
1236 }
1237 (new_selection, edit_type)
1238 }
1239 };
1240 let (text, delta, inval_lines) = buffer.edit([(&selection, "")], edit_type);
1241 let selection = selection.apply_delta(&delta, true, InsertDrift::Default);
1242 cursor.update_selection(buffer, selection);
1243 vec![(text, delta, inval_lines)]
1244 }
1245 DeleteLine => {
1246 let selection = cursor.edit_selection(buffer);
1247 let range = format_start_end(
1248 buffer,
1249 selection.min_offset()..selection.max_offset(),
1250 true,
1251 false,
1252 1,
1253 );
1254 let selection = Selection::region(range.start, range.end, CursorAffinity::Forward);
1255 let (text, delta, inval_lines) = buffer.edit([(&selection, "")], EditType::Delete);
1256 let selection = selection.apply_delta(&delta, true, InsertDrift::Default);
1257 cursor.mode = CursorMode::Insert(selection);
1258 vec![(text, delta, inval_lines)]
1259 }
1260 DeleteWordForward => {
1261 let selection = match cursor.mode {
1262 CursorMode::Normal { .. } | CursorMode::Visual { .. } => {
1263 cursor.edit_selection(buffer)
1264 }
1265 CursorMode::Insert(_) => {
1266 let mut new_selection = Selection::new();
1267 let selection = cursor.edit_selection(buffer);
1268
1269 for region in selection.regions() {
1270 let end = buffer.move_word_forward(region.end);
1271 let new_region =
1272 SelRegion::new(region.start, end, region.affinity, None);
1273 new_selection.add_region(new_region);
1274 }
1275
1276 new_selection
1277 }
1278 };
1279 let (text, delta, inval_lines) =
1280 buffer.edit([(&selection, "")], EditType::DeleteWord);
1281 let selection = selection.apply_delta(&delta, true, InsertDrift::Default);
1282 cursor.update_selection(buffer, selection);
1283 vec![(text, delta, inval_lines)]
1284 }
1285 DeleteWordBackward => {
1286 let selection = match cursor.mode {
1287 CursorMode::Normal { .. } | CursorMode::Visual { .. } => {
1288 cursor.edit_selection(buffer)
1289 }
1290 CursorMode::Insert(_) => {
1291 let mut new_selection = Selection::new();
1292 let selection = cursor.edit_selection(buffer);
1293
1294 for region in selection.regions() {
1295 let end = buffer.move_word_backward_deletion(region.end);
1296 let new_region =
1297 SelRegion::new(region.start, end, region.affinity, None);
1298 new_selection.add_region(new_region);
1299 }
1300
1301 new_selection
1302 }
1303 };
1304 let (text, delta, inval_lines) =
1305 buffer.edit([(&selection, "")], EditType::DeleteWord);
1306 let selection = selection.apply_delta(&delta, true, InsertDrift::Default);
1307 cursor.update_selection(buffer, selection);
1308 vec![(text, delta, inval_lines)]
1309 }
1310 DeleteToBeginningOfLine => {
1311 let selection = match cursor.mode {
1312 CursorMode::Normal { .. } | CursorMode::Visual { .. } => {
1313 cursor.edit_selection(buffer)
1314 }
1315 CursorMode::Insert(_) => {
1316 let selection = cursor.edit_selection(buffer);
1317
1318 let mut new_selection = Selection::new();
1319 for region in selection.regions() {
1320 let line = buffer.line_of_offset(region.end);
1321 let end = buffer.offset_of_line(line);
1322 let new_region =
1323 SelRegion::new(region.start, end, region.affinity, None);
1324 new_selection.add_region(new_region);
1325 }
1326
1327 new_selection
1328 }
1329 };
1330 let (text, delta, inval_lines) =
1331 buffer.edit([(&selection, "")], EditType::DeleteToBeginningOfLine);
1332 let selection = selection.apply_delta(&delta, true, InsertDrift::Default);
1333 cursor.update_selection(buffer, selection);
1334 vec![(text, delta, inval_lines)]
1335 }
1336 DeleteToEndOfLine => {
1337 let selection = match cursor.mode {
1338 CursorMode::Normal { .. } | CursorMode::Visual { .. } => {
1339 cursor.edit_selection(buffer)
1340 }
1341 CursorMode::Insert(_) => {
1342 let mut selection = cursor.edit_selection(buffer);
1343
1344 let cursor_offset = cursor.offset();
1345 let line = buffer.line_of_offset(cursor_offset);
1346 let end_of_line_offset = buffer.line_end_offset(line, true);
1347 let new_region = SelRegion::new(
1348 cursor_offset,
1349 end_of_line_offset,
1350 CursorAffinity::Backward,
1351 None,
1352 );
1353 selection.add_region(new_region);
1354
1355 selection
1356 }
1357 };
1358 let (text, delta, inval_lines) =
1359 buffer.edit([(&selection, "")], EditType::DeleteToEndOfLine);
1360 let selection = selection.apply_delta(&delta, true, InsertDrift::Default);
1361 cursor.update_selection(buffer, selection);
1362 vec![(text, delta, inval_lines)]
1363 }
1364 DeleteForwardAndInsert => {
1365 let selection = cursor.edit_selection(buffer);
1366 let (text, delta, inval_lines) = buffer.edit([(&selection, "")], EditType::Delete);
1367 let selection = selection.apply_delta(&delta, true, InsertDrift::Default);
1368 cursor.mode = CursorMode::Insert(selection);
1369 vec![(text, delta, inval_lines)]
1370 }
1371 DeleteWordAndInsert => {
1372 let selection = {
1373 let mut new_selection = Selection::new();
1374 let selection = cursor.edit_selection(buffer);
1375
1376 for region in selection.regions() {
1377 let end = buffer.move_word_forward(region.end);
1378 let new_region =
1379 SelRegion::new(region.start, end, CursorAffinity::Backward, None);
1380 new_selection.add_region(new_region);
1381 }
1382
1383 new_selection
1384 };
1385 let (text, delta, inval_lines) =
1386 buffer.edit([(&selection, "")], EditType::DeleteWord);
1387 let selection = selection.apply_delta(&delta, true, InsertDrift::Default);
1388 cursor.mode = CursorMode::Insert(selection);
1389 vec![(text, delta, inval_lines)]
1390 }
1391 DeleteLineAndInsert => {
1392 let selection = cursor.edit_selection(buffer);
1393 let range = format_start_end(
1394 buffer,
1395 selection.min_offset()..selection.max_offset(),
1396 true,
1397 true,
1398 1,
1399 );
1400 let selection =
1401 Selection::region(range.start, range.end - 1, CursorAffinity::Backward); let (text, delta, inval_lines) = buffer.edit([(&selection, "")], EditType::Delete);
1403 let selection = selection.apply_delta(&delta, true, InsertDrift::Default);
1404 cursor.mode = CursorMode::Insert(selection);
1405 vec![(text, delta, inval_lines)]
1406 }
1407 DeleteToEndOfLineAndInsert => {
1408 let mut selection = cursor.edit_selection(buffer);
1409
1410 let cursor_offset = cursor.offset();
1411 let line = buffer.line_of_offset(cursor_offset);
1412 let end_of_line_offset = buffer.line_end_offset(line, true);
1413
1414 let new_region = SelRegion::new(
1415 cursor_offset,
1416 end_of_line_offset,
1417 CursorAffinity::Backward,
1418 None,
1419 );
1420 selection.add_region(new_region);
1421
1422 let (text, delta, inval_lines) = buffer.edit([(&selection, "")], EditType::Delete);
1423 let selection = selection.apply_delta(&delta, true, InsertDrift::Default);
1424 cursor.mode = CursorMode::Insert(selection);
1425 vec![(text, delta, inval_lines)]
1426 }
1427 NormalMode => {
1428 if !modal {
1429 if let CursorMode::Insert(selection) = &cursor.mode {
1430 match selection.regions().len() {
1431 i if i > 1 => {
1432 if let Some(region) = selection.last_inserted() {
1433 let new_selection = Selection::region(
1434 region.start,
1435 region.end,
1436 region.affinity,
1437 );
1438 cursor.mode = CursorMode::Insert(new_selection);
1439 return vec![];
1440 }
1441 }
1442 1 => {
1443 let region = selection.regions()[0];
1444 if !region.is_caret() {
1445 let new_selection =
1446 Selection::caret(region.end, region.affinity);
1447 cursor.mode = CursorMode::Insert(new_selection);
1448 return vec![];
1449 }
1450 }
1451 _ => (),
1452 }
1453 }
1454
1455 return vec![];
1456 }
1457
1458 let offset = match &cursor.mode {
1459 CursorMode::Insert(selection) => {
1460 let offset = selection.min_offset();
1461 buffer.prev_grapheme_offset(
1462 offset,
1463 1,
1464 buffer.offset_of_line(buffer.line_of_offset(offset)),
1465 )
1466 }
1467 CursorMode::Visual { end, .. } => buffer.offset_line_end(*end, false).min(*end),
1468 CursorMode::Normal { offset, .. } => *offset,
1469 };
1470 let affinity = cursor.affinity();
1471
1472 buffer.reset_edit_type();
1473 cursor.mode = CursorMode::Normal { offset, affinity };
1474 cursor.horiz = None;
1475 vec![]
1476 }
1477 InsertMode => {
1478 cursor.mode =
1479 CursorMode::Insert(Selection::caret(cursor.offset(), cursor.affinity()));
1480 vec![]
1481 }
1482 InsertFirstNonBlank => {
1483 match &cursor.mode {
1484 CursorMode::Normal { offset, .. } => {
1485 let line = buffer.line_of_offset(*offset);
1486 let offset = buffer.first_non_blank_character_on_line(line);
1487 cursor.mode =
1488 CursorMode::Insert(Selection::caret(offset, CursorAffinity::Forward));
1489 }
1490 CursorMode::Visual { .. } => {
1491 let mut selection = Selection::new();
1492 for region in cursor.edit_selection(buffer).regions() {
1493 selection.add_region(SelRegion::caret(region.min(), region.affinity));
1494 }
1495 cursor.mode = CursorMode::Insert(selection);
1496 }
1497 CursorMode::Insert(_) => {}
1498 };
1499 vec![]
1500 }
1501 Append => {
1502 let offset = cursor.offset();
1503 let line = buffer.line_of_offset(offset);
1504 let line_len = buffer.line_len(line);
1505 let count = (line_len > 1 || (buffer.last_line() == line && line_len > 0)) as usize;
1506 let offset = buffer.move_right(cursor.offset(), Mode::Insert, count);
1507 cursor.mode =
1508 CursorMode::Insert(Selection::caret(offset, CursorAffinity::Backward));
1509 vec![]
1510 }
1511 AppendEndOfLine => {
1512 let offset = cursor.offset();
1513 let line = buffer.line_of_offset(offset);
1514 let offset = buffer.line_end_offset(line, true);
1515 cursor.mode =
1516 CursorMode::Insert(Selection::caret(offset, CursorAffinity::Backward));
1517 vec![]
1518 }
1519 ToggleVisualMode => {
1520 Self::toggle_visual(cursor, VisualMode::Normal, modal);
1521 vec![]
1522 }
1523 ToggleLinewiseVisualMode => {
1524 Self::toggle_visual(cursor, VisualMode::Linewise, modal);
1525 vec![]
1526 }
1527 ToggleBlockwiseVisualMode => {
1528 Self::toggle_visual(cursor, VisualMode::Blockwise, modal);
1529 vec![]
1530 }
1531 DuplicateLineUp => Self::duplicate_line(cursor, buffer, DuplicateDirection::Up),
1532 DuplicateLineDown => Self::duplicate_line(cursor, buffer, DuplicateDirection::Down),
1533 NormalizeLineEndings => {
1534 let Some((text, delta, inval)) = buffer.normalize_line_endings() else {
1535 return vec![];
1536 };
1537
1538 cursor.apply_delta(&delta);
1539
1540 vec![(text, delta, inval)]
1541 }
1542 }
1543 }
1544}
1545
1546fn apply_undo_redo(
1547 cursor: &mut Cursor,
1548 buffer: &mut Buffer,
1549 modal: bool,
1550 text: Rope,
1551 delta: RopeDelta,
1552 inval_lines: InvalLines,
1553 cursor_mode: Option<CursorMode>,
1554) -> Vec<(Rope, RopeDelta, InvalLines)> {
1555 if let Some(cursor_mode) = cursor_mode {
1556 cursor.mode = if modal {
1557 CursorMode::Normal {
1558 offset: cursor_mode.offset(),
1559 affinity: cursor_mode.affinity(),
1560 }
1561 } else if cursor.is_insert() {
1562 cursor_mode
1563 } else {
1564 CursorMode::Insert(Selection::caret(
1565 cursor_mode.offset(),
1566 cursor_mode.affinity(),
1567 ))
1568 };
1569 } else if let Some(new_cursor) = get_first_selection_after(cursor, buffer, &delta) {
1570 *cursor = new_cursor
1571 } else if !delta
1572 .els
1573 .iter()
1574 .any(|el| matches!(el, DeltaElement::Copy(_, _)))
1575 {
1576 } else {
1579 cursor.apply_delta(&delta);
1580 }
1581 vec![(text, delta, inval_lines)]
1582}
1583
1584enum DuplicateDirection {
1585 Up,
1586 Down,
1587}
1588
1589#[cfg(test)]
1590mod test {
1591 use crate::{
1592 buffer::{rope_text::RopeText, Buffer},
1593 cursor::{Cursor, CursorAffinity, CursorMode},
1594 editor::{Action, DuplicateDirection},
1595 selection::{SelRegion, Selection},
1596 word::WordCursor,
1597 };
1598
1599 fn prev_unmatched(buffer: &Buffer, c: char, offset: usize) -> Option<usize> {
1600 WordCursor::new(buffer.text(), offset).previous_unmatched(c)
1601 }
1602
1603 #[test]
1604 fn test_insert_simple() {
1605 let mut buffer = Buffer::new("abc");
1606 let mut cursor = Cursor::new(
1607 CursorMode::Insert(Selection::caret(1, CursorAffinity::Backward)),
1608 None,
1609 None,
1610 );
1611
1612 Action::insert(&mut cursor, &mut buffer, "e", &prev_unmatched, true, true);
1613 assert_eq!("aebc", buffer.slice_to_cow(0..buffer.len()));
1614 }
1615
1616 #[test]
1617 fn test_insert_multiple_cursor() {
1618 let mut buffer = Buffer::new("abc\nefg\n");
1619 let mut selection = Selection::new();
1620 selection.add_region(SelRegion::caret(1, CursorAffinity::Backward));
1621 selection.add_region(SelRegion::caret(5, CursorAffinity::Backward));
1622 let mut cursor = Cursor::new(CursorMode::Insert(selection), None, None);
1623
1624 Action::insert(&mut cursor, &mut buffer, "i", &prev_unmatched, true, true);
1625 assert_eq!("aibc\neifg\n", buffer.slice_to_cow(0..buffer.len()));
1626 }
1627
1628 #[test]
1629 fn test_insert_complex() {
1630 let mut buffer = Buffer::new("abc\nefg\n");
1631 let mut selection = Selection::new();
1632 selection.add_region(SelRegion::caret(1, CursorAffinity::Backward));
1633 selection.add_region(SelRegion::caret(5, CursorAffinity::Backward));
1634 let mut cursor = Cursor::new(CursorMode::Insert(selection), None, None);
1635
1636 Action::insert(&mut cursor, &mut buffer, "i", &prev_unmatched, true, true);
1637 assert_eq!("aibc\neifg\n", buffer.slice_to_cow(0..buffer.len()));
1638 Action::insert(&mut cursor, &mut buffer, "j", &prev_unmatched, true, true);
1639 assert_eq!("aijbc\neijfg\n", buffer.slice_to_cow(0..buffer.len()));
1640 Action::insert(&mut cursor, &mut buffer, "{", &prev_unmatched, true, true);
1641 assert_eq!("aij{bc\neij{fg\n", buffer.slice_to_cow(0..buffer.len()));
1642 Action::insert(&mut cursor, &mut buffer, " ", &prev_unmatched, true, true);
1643 assert_eq!("aij{ bc\neij{ fg\n", buffer.slice_to_cow(0..buffer.len()));
1644 }
1645
1646 #[test]
1647 fn test_insert_pair() {
1648 let mut buffer = Buffer::new("a bc\ne fg\n");
1649 let mut selection = Selection::new();
1650 selection.add_region(SelRegion::caret(1, CursorAffinity::Backward));
1651 selection.add_region(SelRegion::caret(6, CursorAffinity::Backward));
1652 let mut cursor = Cursor::new(CursorMode::Insert(selection), None, None);
1653
1654 Action::insert(&mut cursor, &mut buffer, "{", &prev_unmatched, true, true);
1655 assert_eq!("a{} bc\ne{} fg\n", buffer.slice_to_cow(0..buffer.len()));
1656 Action::insert(&mut cursor, &mut buffer, "}", &prev_unmatched, true, true);
1657 assert_eq!("a{} bc\ne{} fg\n", buffer.slice_to_cow(0..buffer.len()));
1658 }
1659
1660 #[test]
1661 fn test_insert_pair_with_selection() {
1662 let mut buffer = Buffer::new("a bc\ne fg\n");
1663 let mut selection = Selection::new();
1664 selection.add_region(SelRegion::new(0, 4, CursorAffinity::Backward, None));
1665 selection.add_region(SelRegion::new(5, 9, CursorAffinity::Backward, None));
1666 let mut cursor = Cursor::new(CursorMode::Insert(selection), None, None);
1667 Action::insert(&mut cursor, &mut buffer, "{", &prev_unmatched, true, true);
1668 assert_eq!("{a bc}\n{e fg}\n", buffer.slice_to_cow(0..buffer.len()));
1669 }
1670
1671 #[test]
1672 fn test_insert_pair_without_auto_closing() {
1673 let mut buffer = Buffer::new("a bc\ne fg\n");
1674 let mut selection = Selection::new();
1675 selection.add_region(SelRegion::caret(1, CursorAffinity::Backward));
1676 selection.add_region(SelRegion::caret(6, CursorAffinity::Backward));
1677 let mut cursor = Cursor::new(CursorMode::Insert(selection), None, None);
1678
1679 Action::insert(&mut cursor, &mut buffer, "{", &prev_unmatched, false, false);
1680 assert_eq!("a{ bc\ne{ fg\n", buffer.slice_to_cow(0..buffer.len()));
1681 Action::insert(&mut cursor, &mut buffer, "}", &prev_unmatched, false, false);
1682 assert_eq!("a{} bc\ne{} fg\n", buffer.slice_to_cow(0..buffer.len()));
1683 }
1684
1685 #[test]
1686 fn duplicate_down_simple() {
1687 let mut buffer = Buffer::new("first line\nsecond line\n");
1688 let mut selection = Selection::new();
1689 selection.add_region(SelRegion::caret(0, CursorAffinity::Backward));
1690 let mut cursor = Cursor::new(CursorMode::Insert(selection), None, None);
1691
1692 Action::duplicate_line(&mut cursor, &mut buffer, DuplicateDirection::Down);
1693
1694 assert_ne!(cursor.offset(), 0);
1695 assert_eq!(
1696 "first line\nfirst line\nsecond line\n",
1697 buffer.slice_to_cow(0..buffer.len())
1698 );
1699 }
1700
1701 #[test]
1702 fn duplicate_up_simple() {
1703 let mut buffer = Buffer::new("first line\nsecond line\n");
1704 let mut selection = Selection::new();
1705 selection.add_region(SelRegion::caret(0, CursorAffinity::Backward));
1706 let mut cursor = Cursor::new(CursorMode::Insert(selection), None, None);
1707
1708 Action::duplicate_line(&mut cursor, &mut buffer, DuplicateDirection::Up);
1709
1710 assert_eq!(cursor.offset(), 0);
1711 assert_eq!(
1712 "first line\nfirst line\nsecond line\n",
1713 buffer.slice_to_cow(0..buffer.len())
1714 );
1715 }
1716
1717 #[test]
1718 fn duplicate_down_multiple_cursors_in_same_line() {
1719 let mut buffer = Buffer::new("first line\nsecond line\n");
1720 let mut selection = Selection::new();
1721 selection.add_region(SelRegion::caret(0, CursorAffinity::Backward));
1722 selection.add_region(SelRegion::caret(1, CursorAffinity::Backward));
1723 let mut cursor = Cursor::new(CursorMode::Insert(selection), None, None);
1724
1725 Action::duplicate_line(&mut cursor, &mut buffer, DuplicateDirection::Down);
1726
1727 assert_eq!(
1728 "first line\nfirst line\nsecond line\n",
1729 buffer.slice_to_cow(0..buffer.len())
1730 );
1731 }
1732
1733 #[test]
1734 fn duplicate_up_multiple_cursors_in_same_line() {
1735 let mut buffer = Buffer::new("first line\nsecond line\n");
1736 let mut selection = Selection::new();
1737 selection.add_region(SelRegion::caret(0, CursorAffinity::Backward));
1738 selection.add_region(SelRegion::caret(1, CursorAffinity::Backward));
1739 let mut cursor = Cursor::new(CursorMode::Insert(selection), None, None);
1740
1741 Action::duplicate_line(&mut cursor, &mut buffer, DuplicateDirection::Up);
1742
1743 assert_eq!(
1744 "first line\nfirst line\nsecond line\n",
1745 buffer.slice_to_cow(0..buffer.len())
1746 );
1747 }
1748
1749 #[test]
1750 fn duplicate_down_multiple() {
1751 let mut buffer = Buffer::new("first line\nsecond line\n");
1752 let mut selection = Selection::new();
1753 selection.add_region(SelRegion::caret(0, CursorAffinity::Backward));
1754 selection.add_region(SelRegion::caret(15, CursorAffinity::Backward));
1755 let mut cursor = Cursor::new(CursorMode::Insert(selection), None, None);
1756
1757 Action::duplicate_line(&mut cursor, &mut buffer, DuplicateDirection::Down);
1758
1759 assert_eq!(
1760 "first line\nfirst line\nsecond line\nsecond line\n",
1761 buffer.slice_to_cow(0..buffer.len())
1762 );
1763 }
1764
1765 #[test]
1766 fn duplicate_up_multiple() {
1767 let mut buffer = Buffer::new("first line\nsecond line\n");
1768 let mut selection = Selection::new();
1769 selection.add_region(SelRegion::caret(0, CursorAffinity::Backward));
1770 selection.add_region(SelRegion::caret(15, CursorAffinity::Backward));
1771 let mut cursor = Cursor::new(CursorMode::Insert(selection), None, None);
1772
1773 Action::duplicate_line(&mut cursor, &mut buffer, DuplicateDirection::Up);
1774
1775 assert_eq!(
1776 "first line\nfirst line\nsecond line\nsecond line\n",
1777 buffer.slice_to_cow(0..buffer.len())
1778 );
1779 }
1780
1781 #[test]
1782 fn duplicate_down_multiple_with_swapped_cursor_order() {
1783 let mut buffer = Buffer::new("first line\nsecond line\n");
1784 let mut selection = Selection::new();
1785 selection.add_region(SelRegion::caret(15, CursorAffinity::Backward));
1786 selection.add_region(SelRegion::caret(0, CursorAffinity::Backward));
1787 let mut cursor = Cursor::new(CursorMode::Insert(selection), None, None);
1788
1789 Action::duplicate_line(&mut cursor, &mut buffer, DuplicateDirection::Down);
1790
1791 assert_eq!(
1792 "first line\nfirst line\nsecond line\nsecond line\n",
1793 buffer.slice_to_cow(0..buffer.len())
1794 );
1795 }
1796
1797 #[test]
1798 fn duplicate_up_multiple_with_swapped_cursor_order() {
1799 let mut buffer = Buffer::new("first line\nsecond line\n");
1800 let mut selection = Selection::new();
1801 selection.add_region(SelRegion::caret(15, CursorAffinity::Backward));
1802 selection.add_region(SelRegion::caret(0, CursorAffinity::Backward));
1803 let mut cursor = Cursor::new(CursorMode::Insert(selection), None, None);
1804
1805 Action::duplicate_line(&mut cursor, &mut buffer, DuplicateDirection::Up);
1806
1807 assert_eq!(
1808 "first line\nfirst line\nsecond line\nsecond line\n",
1809 buffer.slice_to_cow(0..buffer.len())
1810 );
1811 }
1812
1813 #[test]
1814 fn check_multiple_cursor_match_insertion() {
1815 let mut buffer = Buffer::new(" 123 567 9ab def");
1816 let mut selection = Selection::new();
1817 selection.add_region(SelRegion::caret(0, CursorAffinity::Backward));
1818 selection.add_region(SelRegion::caret(4, CursorAffinity::Backward));
1819 selection.add_region(SelRegion::caret(8, CursorAffinity::Backward));
1820 selection.add_region(SelRegion::caret(12, CursorAffinity::Backward));
1821 let mut cursor = Cursor::new(CursorMode::Insert(selection), None, None);
1822
1823 Action::insert(&mut cursor, &mut buffer, "(", &prev_unmatched, true, true);
1824
1825 assert_eq!(
1826 "() 123() 567() 9ab() def",
1827 buffer.slice_to_cow(0..buffer.len())
1828 );
1829
1830 let mut end_selection = Selection::new();
1831 end_selection.add_region(SelRegion::caret(1, CursorAffinity::Backward));
1832 end_selection.add_region(SelRegion::caret(7, CursorAffinity::Backward));
1833 end_selection.add_region(SelRegion::caret(13, CursorAffinity::Backward));
1834 end_selection.add_region(SelRegion::caret(19, CursorAffinity::Backward));
1835 assert_eq!(cursor.mode, CursorMode::Insert(end_selection));
1836 }
1837
1838 }