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 DeleteSelection => {
1246 let selection = match cursor.mode {
1247 CursorMode::Normal { .. } => cursor.edit_selection(buffer),
1248 CursorMode::Visual { .. } => cursor.edit_selection(buffer),
1249 CursorMode::Insert(_) => cursor.edit_selection(buffer),
1250 };
1251
1252 if selection.regions().iter().all(|r| r.is_caret()) {
1253 return vec![];
1254 }
1255
1256 let (text, delta, inval_lines) =
1257 buffer.edit([(&selection, "")], EditType::DeleteSelection);
1258 let selection = selection.apply_delta(&delta, true, InsertDrift::Default);
1259 cursor.update_selection(buffer, selection);
1260 vec![(text, delta, inval_lines)]
1261 }
1262 DeleteLine => {
1263 let selection = cursor.edit_selection(buffer);
1264 let range = format_start_end(
1265 buffer,
1266 selection.min_offset()..selection.max_offset(),
1267 true,
1268 false,
1269 1,
1270 );
1271 let selection = Selection::region(range.start, range.end, CursorAffinity::Forward);
1272 let (text, delta, inval_lines) = buffer.edit([(&selection, "")], EditType::Delete);
1273 let selection = selection.apply_delta(&delta, true, InsertDrift::Default);
1274 cursor.mode = CursorMode::Insert(selection);
1275 vec![(text, delta, inval_lines)]
1276 }
1277 DeleteWordForward => {
1278 let selection = match cursor.mode {
1279 CursorMode::Normal { .. } | CursorMode::Visual { .. } => {
1280 cursor.edit_selection(buffer)
1281 }
1282 CursorMode::Insert(_) => {
1283 let mut new_selection = Selection::new();
1284 let selection = cursor.edit_selection(buffer);
1285
1286 for region in selection.regions() {
1287 let end = buffer.move_word_forward(region.end);
1288 let new_region =
1289 SelRegion::new(region.start, end, region.affinity, None);
1290 new_selection.add_region(new_region);
1291 }
1292
1293 new_selection
1294 }
1295 };
1296 let (text, delta, inval_lines) =
1297 buffer.edit([(&selection, "")], EditType::DeleteWord);
1298 let selection = selection.apply_delta(&delta, true, InsertDrift::Default);
1299 cursor.update_selection(buffer, selection);
1300 vec![(text, delta, inval_lines)]
1301 }
1302 DeleteWordBackward => {
1303 let selection = match cursor.mode {
1304 CursorMode::Normal { .. } | CursorMode::Visual { .. } => {
1305 cursor.edit_selection(buffer)
1306 }
1307 CursorMode::Insert(_) => {
1308 let mut new_selection = Selection::new();
1309 let selection = cursor.edit_selection(buffer);
1310
1311 for region in selection.regions() {
1312 let end = buffer.move_word_backward_deletion(region.end);
1313 let new_region =
1314 SelRegion::new(region.start, end, region.affinity, None);
1315 new_selection.add_region(new_region);
1316 }
1317
1318 new_selection
1319 }
1320 };
1321 let (text, delta, inval_lines) =
1322 buffer.edit([(&selection, "")], EditType::DeleteWord);
1323 let selection = selection.apply_delta(&delta, true, InsertDrift::Default);
1324 cursor.update_selection(buffer, selection);
1325 vec![(text, delta, inval_lines)]
1326 }
1327 DeleteToBeginningOfLine => {
1328 let selection = match cursor.mode {
1329 CursorMode::Normal { .. } | CursorMode::Visual { .. } => {
1330 cursor.edit_selection(buffer)
1331 }
1332 CursorMode::Insert(_) => {
1333 let selection = cursor.edit_selection(buffer);
1334
1335 let mut new_selection = Selection::new();
1336 for region in selection.regions() {
1337 let line = buffer.line_of_offset(region.end);
1338 let end = buffer.offset_of_line(line);
1339 let new_region =
1340 SelRegion::new(region.start, end, region.affinity, None);
1341 new_selection.add_region(new_region);
1342 }
1343
1344 new_selection
1345 }
1346 };
1347 let (text, delta, inval_lines) =
1348 buffer.edit([(&selection, "")], EditType::DeleteToBeginningOfLine);
1349 let selection = selection.apply_delta(&delta, true, InsertDrift::Default);
1350 cursor.update_selection(buffer, selection);
1351 vec![(text, delta, inval_lines)]
1352 }
1353 DeleteToEndOfLine => {
1354 let selection = match cursor.mode {
1355 CursorMode::Normal { .. } | CursorMode::Visual { .. } => {
1356 cursor.edit_selection(buffer)
1357 }
1358 CursorMode::Insert(_) => {
1359 let mut selection = cursor.edit_selection(buffer);
1360
1361 let cursor_offset = cursor.offset();
1362 let line = buffer.line_of_offset(cursor_offset);
1363 let end_of_line_offset = buffer.line_end_offset(line, true);
1364 let new_region = SelRegion::new(
1365 cursor_offset,
1366 end_of_line_offset,
1367 CursorAffinity::Backward,
1368 None,
1369 );
1370 selection.add_region(new_region);
1371
1372 selection
1373 }
1374 };
1375 let (text, delta, inval_lines) =
1376 buffer.edit([(&selection, "")], EditType::DeleteToEndOfLine);
1377 let selection = selection.apply_delta(&delta, true, InsertDrift::Default);
1378 cursor.update_selection(buffer, selection);
1379 vec![(text, delta, inval_lines)]
1380 }
1381 DeleteForwardAndInsert => {
1382 let selection = cursor.edit_selection(buffer);
1383 let (text, delta, inval_lines) = buffer.edit([(&selection, "")], EditType::Delete);
1384 let selection = selection.apply_delta(&delta, true, InsertDrift::Default);
1385 cursor.mode = CursorMode::Insert(selection);
1386 vec![(text, delta, inval_lines)]
1387 }
1388 DeleteWordAndInsert => {
1389 let selection = {
1390 let mut new_selection = Selection::new();
1391 let selection = cursor.edit_selection(buffer);
1392
1393 for region in selection.regions() {
1394 let end = buffer.move_word_forward(region.end);
1395 let new_region =
1396 SelRegion::new(region.start, end, CursorAffinity::Backward, None);
1397 new_selection.add_region(new_region);
1398 }
1399
1400 new_selection
1401 };
1402 let (text, delta, inval_lines) =
1403 buffer.edit([(&selection, "")], EditType::DeleteWord);
1404 let selection = selection.apply_delta(&delta, true, InsertDrift::Default);
1405 cursor.mode = CursorMode::Insert(selection);
1406 vec![(text, delta, inval_lines)]
1407 }
1408 DeleteLineAndInsert => {
1409 let selection = cursor.edit_selection(buffer);
1410 let range = format_start_end(
1411 buffer,
1412 selection.min_offset()..selection.max_offset(),
1413 true,
1414 true,
1415 1,
1416 );
1417 let selection =
1418 Selection::region(range.start, range.end - 1, CursorAffinity::Backward); let (text, delta, inval_lines) = buffer.edit([(&selection, "")], EditType::Delete);
1420 let selection = selection.apply_delta(&delta, true, InsertDrift::Default);
1421 cursor.mode = CursorMode::Insert(selection);
1422 vec![(text, delta, inval_lines)]
1423 }
1424 DeleteToEndOfLineAndInsert => {
1425 let mut selection = cursor.edit_selection(buffer);
1426
1427 let cursor_offset = cursor.offset();
1428 let line = buffer.line_of_offset(cursor_offset);
1429 let end_of_line_offset = buffer.line_end_offset(line, true);
1430
1431 let new_region = SelRegion::new(
1432 cursor_offset,
1433 end_of_line_offset,
1434 CursorAffinity::Backward,
1435 None,
1436 );
1437 selection.add_region(new_region);
1438
1439 let (text, delta, inval_lines) = buffer.edit([(&selection, "")], EditType::Delete);
1440 let selection = selection.apply_delta(&delta, true, InsertDrift::Default);
1441 cursor.mode = CursorMode::Insert(selection);
1442 vec![(text, delta, inval_lines)]
1443 }
1444 NormalMode => {
1445 if !modal {
1446 if let CursorMode::Insert(selection) = &cursor.mode {
1447 match selection.regions().len() {
1448 i if i > 1 => {
1449 if let Some(region) = selection.last_inserted() {
1450 let new_selection = Selection::region(
1451 region.start,
1452 region.end,
1453 region.affinity,
1454 );
1455 cursor.mode = CursorMode::Insert(new_selection);
1456 return vec![];
1457 }
1458 }
1459 1 => {
1460 let region = selection.regions()[0];
1461 if !region.is_caret() {
1462 let new_selection =
1463 Selection::caret(region.end, region.affinity);
1464 cursor.mode = CursorMode::Insert(new_selection);
1465 return vec![];
1466 }
1467 }
1468 _ => (),
1469 }
1470 }
1471
1472 return vec![];
1473 }
1474
1475 let offset = match &cursor.mode {
1476 CursorMode::Insert(selection) => {
1477 let offset = selection.min_offset();
1478 buffer.prev_grapheme_offset(
1479 offset,
1480 1,
1481 buffer.offset_of_line(buffer.line_of_offset(offset)),
1482 )
1483 }
1484 CursorMode::Visual { end, .. } => buffer.offset_line_end(*end, false).min(*end),
1485 CursorMode::Normal { offset, .. } => *offset,
1486 };
1487 let affinity = cursor.affinity();
1488
1489 buffer.reset_edit_type();
1490 cursor.mode = CursorMode::Normal { offset, affinity };
1491 cursor.horiz = None;
1492 vec![]
1493 }
1494 InsertMode => {
1495 cursor.mode =
1496 CursorMode::Insert(Selection::caret(cursor.offset(), cursor.affinity()));
1497 vec![]
1498 }
1499 InsertFirstNonBlank => {
1500 match &cursor.mode {
1501 CursorMode::Normal { offset, .. } => {
1502 let line = buffer.line_of_offset(*offset);
1503 let offset = buffer.first_non_blank_character_on_line(line);
1504 cursor.mode =
1505 CursorMode::Insert(Selection::caret(offset, CursorAffinity::Forward));
1506 }
1507 CursorMode::Visual { .. } => {
1508 let mut selection = Selection::new();
1509 for region in cursor.edit_selection(buffer).regions() {
1510 selection.add_region(SelRegion::caret(region.min(), region.affinity));
1511 }
1512 cursor.mode = CursorMode::Insert(selection);
1513 }
1514 CursorMode::Insert(_) => {}
1515 };
1516 vec![]
1517 }
1518 Append => {
1519 let offset = cursor.offset();
1520 let line = buffer.line_of_offset(offset);
1521 let line_len = buffer.line_len(line);
1522 let count = (line_len > 1 || (buffer.last_line() == line && line_len > 0)) as usize;
1523 let offset = buffer.move_right(cursor.offset(), Mode::Insert, count);
1524 cursor.mode =
1525 CursorMode::Insert(Selection::caret(offset, CursorAffinity::Backward));
1526 vec![]
1527 }
1528 AppendEndOfLine => {
1529 let offset = cursor.offset();
1530 let line = buffer.line_of_offset(offset);
1531 let offset = buffer.line_end_offset(line, true);
1532 cursor.mode =
1533 CursorMode::Insert(Selection::caret(offset, CursorAffinity::Backward));
1534 vec![]
1535 }
1536 ToggleVisualMode => {
1537 Self::toggle_visual(cursor, VisualMode::Normal, modal);
1538 vec![]
1539 }
1540 ToggleLinewiseVisualMode => {
1541 Self::toggle_visual(cursor, VisualMode::Linewise, modal);
1542 vec![]
1543 }
1544 ToggleBlockwiseVisualMode => {
1545 Self::toggle_visual(cursor, VisualMode::Blockwise, modal);
1546 vec![]
1547 }
1548 DuplicateLineUp => Self::duplicate_line(cursor, buffer, DuplicateDirection::Up),
1549 DuplicateLineDown => Self::duplicate_line(cursor, buffer, DuplicateDirection::Down),
1550 NormalizeLineEndings => {
1551 let Some((text, delta, inval)) = buffer.normalize_line_endings() else {
1552 return vec![];
1553 };
1554
1555 cursor.apply_delta(&delta);
1556
1557 vec![(text, delta, inval)]
1558 }
1559 }
1560 }
1561}
1562
1563fn apply_undo_redo(
1564 cursor: &mut Cursor,
1565 buffer: &mut Buffer,
1566 modal: bool,
1567 text: Rope,
1568 delta: RopeDelta,
1569 inval_lines: InvalLines,
1570 cursor_mode: Option<CursorMode>,
1571) -> Vec<(Rope, RopeDelta, InvalLines)> {
1572 if let Some(cursor_mode) = cursor_mode {
1573 cursor.mode = if modal {
1574 CursorMode::Normal {
1575 offset: cursor_mode.offset(),
1576 affinity: cursor_mode.affinity(),
1577 }
1578 } else if cursor.is_insert() {
1579 cursor_mode
1580 } else {
1581 CursorMode::Insert(Selection::caret(
1582 cursor_mode.offset(),
1583 cursor_mode.affinity(),
1584 ))
1585 };
1586 } else if let Some(new_cursor) = get_first_selection_after(cursor, buffer, &delta) {
1587 *cursor = new_cursor
1588 } else if !delta
1589 .els
1590 .iter()
1591 .any(|el| matches!(el, DeltaElement::Copy(_, _)))
1592 {
1593 } else {
1596 cursor.apply_delta(&delta);
1597 }
1598 vec![(text, delta, inval_lines)]
1599}
1600
1601enum DuplicateDirection {
1602 Up,
1603 Down,
1604}
1605
1606#[cfg(test)]
1607mod test {
1608 use crate::{
1609 buffer::{rope_text::RopeText, Buffer},
1610 cursor::{Cursor, CursorAffinity, CursorMode},
1611 editor::{Action, DuplicateDirection},
1612 selection::{SelRegion, Selection},
1613 word::WordCursor,
1614 };
1615
1616 fn prev_unmatched(buffer: &Buffer, c: char, offset: usize) -> Option<usize> {
1617 WordCursor::new(buffer.text(), offset).previous_unmatched(c)
1618 }
1619
1620 #[test]
1621 fn test_insert_simple() {
1622 let mut buffer = Buffer::new("abc");
1623 let mut cursor = Cursor::new(
1624 CursorMode::Insert(Selection::caret(1, CursorAffinity::Backward)),
1625 None,
1626 None,
1627 );
1628
1629 Action::insert(&mut cursor, &mut buffer, "e", &prev_unmatched, true, true);
1630 assert_eq!("aebc", buffer.slice_to_cow(0..buffer.len()));
1631 }
1632
1633 #[test]
1634 fn test_insert_multiple_cursor() {
1635 let mut buffer = Buffer::new("abc\nefg\n");
1636 let mut selection = Selection::new();
1637 selection.add_region(SelRegion::caret(1, CursorAffinity::Backward));
1638 selection.add_region(SelRegion::caret(5, CursorAffinity::Backward));
1639 let mut cursor = Cursor::new(CursorMode::Insert(selection), None, None);
1640
1641 Action::insert(&mut cursor, &mut buffer, "i", &prev_unmatched, true, true);
1642 assert_eq!("aibc\neifg\n", buffer.slice_to_cow(0..buffer.len()));
1643 }
1644
1645 #[test]
1646 fn test_insert_complex() {
1647 let mut buffer = Buffer::new("abc\nefg\n");
1648 let mut selection = Selection::new();
1649 selection.add_region(SelRegion::caret(1, CursorAffinity::Backward));
1650 selection.add_region(SelRegion::caret(5, CursorAffinity::Backward));
1651 let mut cursor = Cursor::new(CursorMode::Insert(selection), None, None);
1652
1653 Action::insert(&mut cursor, &mut buffer, "i", &prev_unmatched, true, true);
1654 assert_eq!("aibc\neifg\n", buffer.slice_to_cow(0..buffer.len()));
1655 Action::insert(&mut cursor, &mut buffer, "j", &prev_unmatched, true, true);
1656 assert_eq!("aijbc\neijfg\n", buffer.slice_to_cow(0..buffer.len()));
1657 Action::insert(&mut cursor, &mut buffer, "{", &prev_unmatched, true, true);
1658 assert_eq!("aij{bc\neij{fg\n", buffer.slice_to_cow(0..buffer.len()));
1659 Action::insert(&mut cursor, &mut buffer, " ", &prev_unmatched, true, true);
1660 assert_eq!("aij{ bc\neij{ fg\n", buffer.slice_to_cow(0..buffer.len()));
1661 }
1662
1663 #[test]
1664 fn test_insert_pair() {
1665 let mut buffer = Buffer::new("a bc\ne fg\n");
1666 let mut selection = Selection::new();
1667 selection.add_region(SelRegion::caret(1, CursorAffinity::Backward));
1668 selection.add_region(SelRegion::caret(6, CursorAffinity::Backward));
1669 let mut cursor = Cursor::new(CursorMode::Insert(selection), None, None);
1670
1671 Action::insert(&mut cursor, &mut buffer, "{", &prev_unmatched, true, true);
1672 assert_eq!("a{} bc\ne{} fg\n", buffer.slice_to_cow(0..buffer.len()));
1673 Action::insert(&mut cursor, &mut buffer, "}", &prev_unmatched, true, true);
1674 assert_eq!("a{} bc\ne{} fg\n", buffer.slice_to_cow(0..buffer.len()));
1675 }
1676
1677 #[test]
1678 fn test_insert_pair_with_selection() {
1679 let mut buffer = Buffer::new("a bc\ne fg\n");
1680 let mut selection = Selection::new();
1681 selection.add_region(SelRegion::new(0, 4, CursorAffinity::Backward, None));
1682 selection.add_region(SelRegion::new(5, 9, CursorAffinity::Backward, None));
1683 let mut cursor = Cursor::new(CursorMode::Insert(selection), None, None);
1684 Action::insert(&mut cursor, &mut buffer, "{", &prev_unmatched, true, true);
1685 assert_eq!("{a bc}\n{e fg}\n", buffer.slice_to_cow(0..buffer.len()));
1686 }
1687
1688 #[test]
1689 fn test_insert_pair_without_auto_closing() {
1690 let mut buffer = Buffer::new("a bc\ne fg\n");
1691 let mut selection = Selection::new();
1692 selection.add_region(SelRegion::caret(1, CursorAffinity::Backward));
1693 selection.add_region(SelRegion::caret(6, CursorAffinity::Backward));
1694 let mut cursor = Cursor::new(CursorMode::Insert(selection), None, None);
1695
1696 Action::insert(&mut cursor, &mut buffer, "{", &prev_unmatched, false, false);
1697 assert_eq!("a{ bc\ne{ fg\n", buffer.slice_to_cow(0..buffer.len()));
1698 Action::insert(&mut cursor, &mut buffer, "}", &prev_unmatched, false, false);
1699 assert_eq!("a{} bc\ne{} fg\n", buffer.slice_to_cow(0..buffer.len()));
1700 }
1701
1702 #[test]
1703 fn duplicate_down_simple() {
1704 let mut buffer = Buffer::new("first line\nsecond line\n");
1705 let mut selection = Selection::new();
1706 selection.add_region(SelRegion::caret(0, CursorAffinity::Backward));
1707 let mut cursor = Cursor::new(CursorMode::Insert(selection), None, None);
1708
1709 Action::duplicate_line(&mut cursor, &mut buffer, DuplicateDirection::Down);
1710
1711 assert_ne!(cursor.offset(), 0);
1712 assert_eq!(
1713 "first line\nfirst line\nsecond line\n",
1714 buffer.slice_to_cow(0..buffer.len())
1715 );
1716 }
1717
1718 #[test]
1719 fn duplicate_up_simple() {
1720 let mut buffer = Buffer::new("first line\nsecond line\n");
1721 let mut selection = Selection::new();
1722 selection.add_region(SelRegion::caret(0, CursorAffinity::Backward));
1723 let mut cursor = Cursor::new(CursorMode::Insert(selection), None, None);
1724
1725 Action::duplicate_line(&mut cursor, &mut buffer, DuplicateDirection::Up);
1726
1727 assert_eq!(cursor.offset(), 0);
1728 assert_eq!(
1729 "first line\nfirst line\nsecond line\n",
1730 buffer.slice_to_cow(0..buffer.len())
1731 );
1732 }
1733
1734 #[test]
1735 fn duplicate_down_multiple_cursors_in_same_line() {
1736 let mut buffer = Buffer::new("first line\nsecond line\n");
1737 let mut selection = Selection::new();
1738 selection.add_region(SelRegion::caret(0, CursorAffinity::Backward));
1739 selection.add_region(SelRegion::caret(1, CursorAffinity::Backward));
1740 let mut cursor = Cursor::new(CursorMode::Insert(selection), None, None);
1741
1742 Action::duplicate_line(&mut cursor, &mut buffer, DuplicateDirection::Down);
1743
1744 assert_eq!(
1745 "first line\nfirst line\nsecond line\n",
1746 buffer.slice_to_cow(0..buffer.len())
1747 );
1748 }
1749
1750 #[test]
1751 fn duplicate_up_multiple_cursors_in_same_line() {
1752 let mut buffer = Buffer::new("first line\nsecond line\n");
1753 let mut selection = Selection::new();
1754 selection.add_region(SelRegion::caret(0, CursorAffinity::Backward));
1755 selection.add_region(SelRegion::caret(1, CursorAffinity::Backward));
1756 let mut cursor = Cursor::new(CursorMode::Insert(selection), None, None);
1757
1758 Action::duplicate_line(&mut cursor, &mut buffer, DuplicateDirection::Up);
1759
1760 assert_eq!(
1761 "first line\nfirst line\nsecond line\n",
1762 buffer.slice_to_cow(0..buffer.len())
1763 );
1764 }
1765
1766 #[test]
1767 fn duplicate_down_multiple() {
1768 let mut buffer = Buffer::new("first line\nsecond line\n");
1769 let mut selection = Selection::new();
1770 selection.add_region(SelRegion::caret(0, CursorAffinity::Backward));
1771 selection.add_region(SelRegion::caret(15, CursorAffinity::Backward));
1772 let mut cursor = Cursor::new(CursorMode::Insert(selection), None, None);
1773
1774 Action::duplicate_line(&mut cursor, &mut buffer, DuplicateDirection::Down);
1775
1776 assert_eq!(
1777 "first line\nfirst line\nsecond line\nsecond line\n",
1778 buffer.slice_to_cow(0..buffer.len())
1779 );
1780 }
1781
1782 #[test]
1783 fn duplicate_up_multiple() {
1784 let mut buffer = Buffer::new("first line\nsecond line\n");
1785 let mut selection = Selection::new();
1786 selection.add_region(SelRegion::caret(0, CursorAffinity::Backward));
1787 selection.add_region(SelRegion::caret(15, CursorAffinity::Backward));
1788 let mut cursor = Cursor::new(CursorMode::Insert(selection), None, None);
1789
1790 Action::duplicate_line(&mut cursor, &mut buffer, DuplicateDirection::Up);
1791
1792 assert_eq!(
1793 "first line\nfirst line\nsecond line\nsecond line\n",
1794 buffer.slice_to_cow(0..buffer.len())
1795 );
1796 }
1797
1798 #[test]
1799 fn duplicate_down_multiple_with_swapped_cursor_order() {
1800 let mut buffer = Buffer::new("first line\nsecond line\n");
1801 let mut selection = Selection::new();
1802 selection.add_region(SelRegion::caret(15, CursorAffinity::Backward));
1803 selection.add_region(SelRegion::caret(0, CursorAffinity::Backward));
1804 let mut cursor = Cursor::new(CursorMode::Insert(selection), None, None);
1805
1806 Action::duplicate_line(&mut cursor, &mut buffer, DuplicateDirection::Down);
1807
1808 assert_eq!(
1809 "first line\nfirst line\nsecond line\nsecond line\n",
1810 buffer.slice_to_cow(0..buffer.len())
1811 );
1812 }
1813
1814 #[test]
1815 fn duplicate_up_multiple_with_swapped_cursor_order() {
1816 let mut buffer = Buffer::new("first line\nsecond line\n");
1817 let mut selection = Selection::new();
1818 selection.add_region(SelRegion::caret(15, CursorAffinity::Backward));
1819 selection.add_region(SelRegion::caret(0, CursorAffinity::Backward));
1820 let mut cursor = Cursor::new(CursorMode::Insert(selection), None, None);
1821
1822 Action::duplicate_line(&mut cursor, &mut buffer, DuplicateDirection::Up);
1823
1824 assert_eq!(
1825 "first line\nfirst line\nsecond line\nsecond line\n",
1826 buffer.slice_to_cow(0..buffer.len())
1827 );
1828 }
1829
1830 #[test]
1831 fn check_multiple_cursor_match_insertion() {
1832 let mut buffer = Buffer::new(" 123 567 9ab def");
1833 let mut selection = Selection::new();
1834 selection.add_region(SelRegion::caret(0, CursorAffinity::Backward));
1835 selection.add_region(SelRegion::caret(4, CursorAffinity::Backward));
1836 selection.add_region(SelRegion::caret(8, CursorAffinity::Backward));
1837 selection.add_region(SelRegion::caret(12, CursorAffinity::Backward));
1838 let mut cursor = Cursor::new(CursorMode::Insert(selection), None, None);
1839
1840 Action::insert(&mut cursor, &mut buffer, "(", &prev_unmatched, true, true);
1841
1842 assert_eq!(
1843 "() 123() 567() 9ab() def",
1844 buffer.slice_to_cow(0..buffer.len())
1845 );
1846
1847 let mut end_selection = Selection::new();
1848 end_selection.add_region(SelRegion::caret(1, CursorAffinity::Backward));
1849 end_selection.add_region(SelRegion::caret(7, CursorAffinity::Backward));
1850 end_selection.add_region(SelRegion::caret(13, CursorAffinity::Backward));
1851 end_selection.add_region(SelRegion::caret(19, CursorAffinity::Backward));
1852 assert_eq!(cursor.mode, CursorMode::Insert(end_selection));
1853 }
1854
1855 }