1use std::{collections::HashSet, iter, ops::Range};
2
3use itertools::Itertools;
4use lapce_xi_rope::{DeltaElement, Rope, RopeDelta};
5
6use crate::{
7 buffer::{Buffer, InvalLines, rope_text::RopeText},
8 command::EditCommand,
9 cursor::{Cursor, CursorAffinity, CursorMode, get_first_selection_after},
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::{CharClassification, get_char_property},
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 && let Some(true) = matching_pair_direction(c)
391 && let Some(c) = matching_char(c)
392 && second_half_trim.starts_with(c)
393 {
394 let selection = 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 let edits = edits
401 .iter()
402 .map(|(selection, s)| (selection, s.as_str()))
403 .collect::<Vec<_>>();
404 let (text, delta, inval_lines) = buffer.edit(&edits, EditType::InsertNewline);
405 let mut selection = selection.apply_delta(&delta, true, InsertDrift::Default);
406
407 let mut deltas = vec![(text, delta, inval_lines)];
408
409 if !extra_edits.is_empty() {
410 let edits = extra_edits
411 .iter()
412 .map(|(selection, s)| (selection, s.as_str()))
413 .collect::<Vec<_>>();
414 let (text, delta, inval_lines) = buffer.edit(&edits, EditType::InsertNewline);
415 selection = selection.apply_delta(&delta, false, InsertDrift::Default);
416 deltas.push((text, delta, inval_lines));
417 }
418
419 cursor.mode = CursorMode::Insert(selection);
420
421 deltas
422 }
423
424 pub fn execute_motion_mode(
425 cursor: &mut Cursor,
426 buffer: &mut Buffer,
427 motion_mode: MotionMode,
428 range: Range<usize>,
429 is_vertical: bool,
430 register: &mut Register,
431 ) -> Vec<(Rope, RopeDelta, InvalLines)> {
432 let mut deltas = Vec::new();
433 match motion_mode {
434 MotionMode::Delete { .. } => {
435 let range = format_start_end(buffer, range, is_vertical, false, 1);
436 register.add(
437 RegisterKind::Delete,
438 RegisterData {
439 content: buffer.slice_to_cow(range.clone()).to_string(),
440 mode: if is_vertical {
441 VisualMode::Linewise
442 } else {
443 VisualMode::Normal
444 },
445 },
446 );
447 let selection = Selection::region(range.start, range.end, CursorAffinity::Forward);
448 let (text, delta, inval_lines) =
449 buffer.edit([(&selection, "")], EditType::MotionDelete);
450 cursor.apply_delta(&delta);
451 deltas.push((text, delta, inval_lines));
452 }
453 MotionMode::Yank { .. } => {
454 let range = format_start_end(buffer, range, is_vertical, false, 1);
455 register.add(
456 RegisterKind::Yank,
457 RegisterData {
458 content: buffer.slice_to_cow(range).to_string(),
459 mode: if is_vertical {
460 VisualMode::Linewise
461 } else {
462 VisualMode::Normal
463 },
464 },
465 );
466 }
467 MotionMode::Indent => {
468 let selection = Selection::region(range.start, range.end, CursorAffinity::Backward);
469 let (text, delta, inval_lines) = Self::do_indent(buffer, selection);
470 deltas.push((text, delta, inval_lines));
471 }
472 MotionMode::Outdent => {
473 let selection = Selection::region(range.start, range.end, CursorAffinity::Forward);
474 let (text, delta, inval_lines) = Self::do_outdent(buffer, selection);
475 deltas.push((text, delta, inval_lines));
476 }
477 }
478 deltas
479 }
480
481 fn compute_paste_edit(
487 buffer: &mut Buffer,
488 selection: &Selection,
489 content: &str,
490 mode: VisualMode,
491 ) -> (Rope, RopeDelta, InvalLines) {
492 if selection.len() > 1 {
493 let line_ends: Vec<_> = content.match_indices('\n').map(|(idx, _)| idx).collect();
494
495 match mode {
496 VisualMode::Normal if (line_ends.len() + 1) % selection.len() == 0 => {
500 let lines_per_group = (line_ends.len() + 1) / selection.len();
501 let mut start_idx = 0;
502 let last_line_start = line_ends
503 .len()
504 .checked_sub(lines_per_group)
505 .and_then(|line_idx| line_ends.get(line_idx))
506 .map(|line_end| line_end + 1)
507 .unwrap_or(0);
508
509 let groups = line_ends
510 .iter()
511 .skip(lines_per_group - 1)
512 .step_by(lines_per_group)
513 .map(|&end_idx| {
514 let group = &content[start_idx..end_idx];
515 let group = group.strip_suffix('\r').unwrap_or(group);
516 start_idx = end_idx + 1;
517
518 group
519 })
520 .chain(iter::once(&content[last_line_start..]));
521
522 let edits = selection
523 .regions()
524 .iter()
525 .copied()
526 .map(Selection::sel_region)
527 .zip(groups);
528
529 buffer.edit(edits, EditType::Paste)
530 }
531 VisualMode::Linewise | VisualMode::Blockwise
535 if line_ends.len() % selection.len() == 0 =>
536 {
537 let lines_per_group = line_ends.len() / selection.len();
538 let mut start_idx = 0;
539
540 let groups = line_ends
541 .iter()
542 .skip(lines_per_group - 1)
543 .step_by(lines_per_group)
544 .map(|&end_idx| {
545 let group = &content[start_idx..=end_idx];
546 start_idx = end_idx + 1;
547
548 group
549 });
550
551 let edits = selection
552 .regions()
553 .iter()
554 .copied()
555 .map(Selection::sel_region)
556 .zip(groups);
557
558 buffer.edit(edits, EditType::Paste)
559 }
560 _ => buffer.edit([(&selection, content)], EditType::Paste),
561 }
562 } else {
563 buffer.edit([(&selection, content)], EditType::Paste)
564 }
565 }
566
567 pub fn do_paste(
568 cursor: &mut Cursor,
569 buffer: &mut Buffer,
570 data: &RegisterData,
571 ) -> Vec<(Rope, RopeDelta, InvalLines)> {
572 let mut deltas = Vec::new();
573 match data.mode {
574 VisualMode::Normal => {
575 let selection = match cursor.mode {
576 CursorMode::Normal { offset, affinity } => {
577 let line_end = buffer.offset_line_end(offset, true);
578 let offset = (offset + 1).min(line_end);
579 Selection::caret(offset, affinity)
580 }
581 CursorMode::Insert { .. } | CursorMode::Visual { .. } => {
582 cursor.edit_selection(buffer)
583 }
584 };
585 let after = cursor.is_insert() || !data.content.contains('\n');
586 let (text, delta, inval_lines) =
587 Self::compute_paste_edit(buffer, &selection, &data.content, data.mode);
588 let selection = selection.apply_delta(&delta, after, InsertDrift::Default);
589 deltas.push((text, delta, inval_lines));
590 if !after {
591 cursor.update_selection(buffer, selection);
592 } else {
593 match cursor.mode {
594 CursorMode::Normal { .. } | CursorMode::Visual { .. } => {
595 let offset = buffer.prev_grapheme_offset(selection.min_offset(), 1, 0);
596 cursor.mode = CursorMode::Normal {
597 offset,
598 affinity: selection.get_cursor_affinity(),
599 };
600 }
601 CursorMode::Insert { .. } => {
602 cursor.mode = CursorMode::Insert(selection);
603 }
604 }
605 }
606 }
607 VisualMode::Linewise | VisualMode::Blockwise => {
608 let (selection, content) = match &cursor.mode {
609 CursorMode::Normal { offset, .. } => {
610 let line = buffer.line_of_offset(*offset);
611 let offset = buffer.offset_of_line(line + 1);
612 (
613 Selection::caret(offset, CursorAffinity::Backward),
614 data.content.clone(),
615 )
616 }
617 CursorMode::Insert(selection) => {
618 let mut selection = selection.clone();
619 for region in selection.regions_mut() {
620 if region.is_caret() {
621 let line = buffer.line_of_offset(region.start);
622 let start = buffer.offset_of_line(line);
623 region.start = start;
624 region.end = start;
625 }
626 }
627 (selection, data.content.clone())
628 }
629 CursorMode::Visual { mode, .. } => {
630 let selection = cursor.edit_selection(buffer);
631 let data = match mode {
632 VisualMode::Linewise => data.content.clone(),
633 _ => "\n".to_string() + &data.content,
634 };
635 (selection, data)
636 }
637 };
638 let (text, delta, inval_lines) =
639 Self::compute_paste_edit(buffer, &selection, &content, data.mode);
640 let selection =
641 selection.apply_delta(&delta, cursor.is_insert(), InsertDrift::Default);
642 deltas.push((text, delta, inval_lines));
643 match cursor.mode {
644 CursorMode::Normal { .. } | CursorMode::Visual { .. } => {
645 let offset = selection.min_offset();
646 let offset = if cursor.is_visual() {
647 offset + 1
648 } else {
649 offset
650 };
651 let line = buffer.line_of_offset(offset);
652 let offset = buffer.first_non_blank_character_on_line(line);
653 cursor.mode = CursorMode::Normal {
654 offset,
655 affinity: selection.get_cursor_affinity(),
656 };
657 }
658 CursorMode::Insert(_) => {
659 cursor.mode = CursorMode::Insert(selection);
660 }
661 }
662 }
663 }
664 deltas
665 }
666
667 fn do_indent(buffer: &mut Buffer, selection: Selection) -> (Rope, RopeDelta, InvalLines) {
668 let indent = buffer.indent_unit();
669 let mut edits = Vec::new();
670
671 let mut lines = HashSet::new();
672 for region in selection.regions() {
673 let start_line = buffer.line_of_offset(region.min());
674 let mut end_line = buffer.line_of_offset(region.max());
675 if end_line > start_line {
676 let end_line_start = buffer.offset_of_line(end_line);
677 if end_line_start == region.max() {
678 end_line -= 1;
679 }
680 }
681 for line in start_line..=end_line {
682 if lines.insert(line) {
683 let line_content = buffer.line_content(line);
684 if line_content == "\n" || line_content == "\r\n" {
685 continue;
686 }
687 let nonblank = buffer.first_non_blank_character_on_line(line);
688 let edit = crate::indent::create_edit(buffer, nonblank, indent);
689 edits.push(edit);
690 }
691 }
692 }
693
694 buffer.edit(&edits, EditType::Indent)
695 }
696
697 fn do_outdent(buffer: &mut Buffer, selection: Selection) -> (Rope, RopeDelta, InvalLines) {
698 let indent = buffer.indent_unit();
699 let mut edits = Vec::new();
700
701 let mut lines = HashSet::new();
702 for region in selection.regions() {
703 let start_line = buffer.line_of_offset(region.min());
704 let mut end_line = buffer.line_of_offset(region.max());
705 if end_line > start_line {
706 let end_line_start = buffer.offset_of_line(end_line);
707 if end_line_start == region.max() {
708 end_line -= 1;
709 }
710 }
711 for line in start_line..=end_line {
712 if lines.insert(line) {
713 let line_content = buffer.line_content(line);
714 if line_content == "\n" || line_content == "\r\n" {
715 continue;
716 }
717 let nonblank = buffer.first_non_blank_character_on_line(line);
718 if let Some(edit) = crate::indent::create_outdent(buffer, nonblank, indent) {
719 edits.push(edit);
720 }
721 }
722 }
723 }
724
725 buffer.edit(&edits, EditType::Outdent)
726 }
727
728 fn duplicate_line(
729 cursor: &mut Cursor,
730 buffer: &mut Buffer,
731 direction: DuplicateDirection,
732 ) -> Vec<(Rope, RopeDelta, InvalLines)> {
733 let selection = match cursor.mode {
735 CursorMode::Insert(ref mut sel) => sel,
736 _ => return vec![],
737 };
738
739 let mut line_ranges = HashSet::new();
740 for region in selection.regions_mut() {
741 let start_line = buffer.line_of_offset(region.start);
742 let end_line = buffer.line_of_offset(region.end) + 1;
743
744 line_ranges.insert(start_line..end_line);
745 }
746
747 let mut edits = vec![];
748 for range in line_ranges {
749 let start = buffer.offset_of_line(range.start);
750 let end = buffer.offset_of_line(range.end);
751
752 let content = buffer.slice_to_cow(start..end).into_owned();
753 edits.push((
754 match direction {
755 DuplicateDirection::Up => Selection::caret(end, CursorAffinity::Backward),
756 DuplicateDirection::Down => Selection::caret(start, CursorAffinity::Backward),
757 },
758 content,
759 ));
760 }
761
762 let edits = edits
763 .iter()
764 .map(|(sel, content)| (sel, content.as_str()))
765 .collect::<Vec<_>>();
766
767 let (text, delta, inval_lines) = buffer.edit(&edits, EditType::InsertChars);
768
769 *selection = selection.apply_delta(&delta, true, InsertDrift::Default);
770
771 vec![(text, delta, inval_lines)]
772 }
773
774 #[allow(clippy::too_many_arguments)]
775 pub fn do_edit<T: Clipboard>(
776 cursor: &mut Cursor,
777 buffer: &mut Buffer,
778 cmd: &EditCommand,
779 clipboard: &mut T,
780 register: &mut Register,
781 EditConf {
782 comment_token,
783 modal,
784 smart_tab,
785 keep_indent,
786 auto_indent,
787 }: EditConf,
788 ) -> Vec<(Rope, RopeDelta, InvalLines)> {
789 use crate::command::EditCommand::*;
790 match cmd {
791 MoveLineUp => {
792 let mut deltas = Vec::new();
793 if let CursorMode::Insert(mut selection) = cursor.mode.clone() {
794 for region in selection.regions_mut() {
795 let start_line = buffer.line_of_offset(region.min());
796 if start_line > 0 {
797 let previous_line_len = buffer.line_content(start_line - 1).len();
798
799 let end_line = buffer.line_of_offset(region.max());
800 let start = buffer.offset_of_line(start_line);
801 let end = buffer.offset_of_line(end_line + 1);
802 let content = buffer.slice_to_cow(start..end).to_string();
803 let (text, delta, inval_lines) = buffer.edit(
804 [
805 (&Selection::region(start, end, region.affinity), ""),
806 (
807 &Selection::caret(
808 buffer.offset_of_line(start_line - 1),
809 region.affinity,
810 ),
811 &content,
812 ),
813 ],
814 EditType::MoveLine,
815 );
816 deltas.push((text, delta, inval_lines));
817 region.start -= previous_line_len;
818 region.end -= previous_line_len;
819 }
820 }
821 cursor.mode = CursorMode::Insert(selection);
822 }
823 deltas
824 }
825 MoveLineDown => {
826 let mut deltas = Vec::new();
827 if let CursorMode::Insert(mut selection) = cursor.mode.clone() {
828 for region in selection.regions_mut().iter_mut().rev() {
829 let last_line = buffer.last_line();
830 let start_line = buffer.line_of_offset(region.min());
831 let end_line = buffer.line_of_offset(region.max());
832 if end_line < last_line {
833 let next_line_len = buffer.line_content(end_line + 1).len();
834
835 let start = buffer.offset_of_line(start_line);
836 let end = buffer.offset_of_line(end_line + 1);
837 let content = buffer.slice_to_cow(start..end).to_string();
838 let (text, delta, inval_lines) = buffer.edit(
839 [
840 (
841 &Selection::caret(
842 buffer.offset_of_line(end_line + 2),
843 region.affinity,
844 ),
845 content.as_str(),
846 ),
847 (&Selection::region(start, end, region.affinity), ""),
848 ],
849 EditType::MoveLine,
850 );
851 deltas.push((text, delta, inval_lines));
852 region.start += next_line_len;
853 region.end += next_line_len;
854 }
855 }
856 cursor.mode = CursorMode::Insert(selection);
857 }
858 deltas
859 }
860 InsertNewLine => match cursor.mode.clone() {
861 CursorMode::Normal { offset, .. } => Self::insert_new_line(
862 buffer,
863 cursor,
864 Selection::imaginary_caret(offset),
865 keep_indent,
866 auto_indent,
867 ),
868 CursorMode::Insert(selection) => {
869 Self::insert_new_line(buffer, cursor, selection, keep_indent, auto_indent)
870 }
871 CursorMode::Visual { .. } => {
872 vec![]
873 }
874 },
875 InsertTab => {
876 let mut deltas = Vec::new();
877 if let CursorMode::Insert(selection) = &cursor.mode {
878 if smart_tab {
879 let indent = buffer.indent_unit();
880 let mut edits = Vec::new();
881
882 for region in selection.regions() {
883 if region.is_caret() {
884 edits.push(crate::indent::create_edit(buffer, region.start, indent))
885 } else {
886 let start_line = buffer.line_of_offset(region.min());
887 let end_line = buffer.line_of_offset(region.max());
888 for line in start_line..=end_line {
889 let offset = buffer.first_non_blank_character_on_line(line);
890 edits.push(crate::indent::create_edit(buffer, offset, indent))
891 }
892 }
893 }
894
895 let (text, delta, inval_lines) = buffer.edit(&edits, EditType::InsertChars);
896 let selection = selection.apply_delta(&delta, true, InsertDrift::Default);
897 deltas.push((text, delta, inval_lines));
898 cursor.mode = CursorMode::Insert(selection);
899 } else {
900 let (text, delta, inval_lines) =
901 buffer.edit([(&selection, "\t")], EditType::InsertChars);
902 let selection = selection.apply_delta(&delta, true, InsertDrift::Default);
903 deltas.push((text, delta, inval_lines));
904 cursor.mode = CursorMode::Insert(selection);
905 }
906 }
907 deltas
908 }
909 IndentLine => {
910 let selection = cursor.edit_selection(buffer);
911 let (text, delta, inval_lines) = Self::do_indent(buffer, selection);
912 cursor.apply_delta(&delta);
913 vec![(text, delta, inval_lines)]
914 }
915 JoinLines => {
916 let offset = cursor.offset();
917 let (line, _col) = buffer.offset_to_line_col(offset);
918 if line < buffer.last_line() {
919 let start = buffer.line_end_offset(line, true);
920 let end = buffer.first_non_blank_character_on_line(line + 1);
921 vec![buffer.edit(
922 [(&Selection::imaginary_region(start, end), " ")],
923 EditType::Other,
924 )]
925 } else {
926 vec![]
927 }
928 }
929 OutdentLine => {
930 let selection = cursor.edit_selection(buffer);
931 let (text, delta, inval_lines) = Self::do_outdent(buffer, selection);
932 cursor.apply_delta(&delta);
933 vec![(text, delta, inval_lines)]
934 }
935 ToggleLineComment => {
936 let mut lines = HashSet::new();
937 let selection = cursor.edit_selection(buffer);
938 let mut had_comment = true;
939 let mut smallest_indent = usize::MAX;
940 for region in selection.regions() {
941 let mut line = buffer.line_of_offset(region.min());
942 let end_line = buffer.line_of_offset(region.max());
943 let end_line_offset = buffer.offset_of_line(end_line);
944 let end = if end_line > line && region.max() == end_line_offset {
945 end_line_offset
946 } else {
947 buffer.offset_of_line(end_line + 1)
948 };
949 let start = buffer.offset_of_line(line);
950 for content in buffer.text().lines(start..end) {
951 let trimmed_content = content.trim_start();
952 if trimmed_content.is_empty() {
953 line += 1;
954 continue;
955 }
956 let indent = content.len() - trimmed_content.len();
957 if indent < smallest_indent {
958 smallest_indent = indent;
959 }
960 if !trimmed_content.starts_with(comment_token) {
961 had_comment = false;
962 lines.insert((line, indent, 0));
963 } else {
964 let had_space_after_comment =
965 trimmed_content.chars().nth(comment_token.len()) == Some(' ');
966 lines.insert((
967 line,
968 indent,
969 comment_token.len() + usize::from(had_space_after_comment),
970 ));
971 }
972 line += 1;
973 }
974 }
975
976 let (text, delta, inval_lines) = if had_comment {
977 let mut selection = Selection::new();
978 for (line, indent, len) in lines.iter() {
979 let start = buffer.offset_of_line(*line) + indent;
980 selection.add_region(SelRegion::imaginary_region(start, start + len))
981 }
982 buffer.edit([(&selection, "")], EditType::ToggleComment)
983 } else {
984 let mut selection = Selection::new();
985 for (line, _, _) in lines.iter() {
986 let start = buffer.offset_of_line(*line) + smallest_indent;
987 selection.add_region(SelRegion::imaginary_caret(start))
988 }
989 buffer.edit(
990 [(&selection, format!("{comment_token} ").as_str())],
991 EditType::ToggleComment,
992 )
993 };
994 cursor.apply_delta(&delta);
995 vec![(text, delta, inval_lines)]
996 }
997 Undo => {
998 if let Some((text, delta, inval_lines, cursor_mode)) = buffer.do_undo() {
999 apply_undo_redo(cursor, buffer, modal, text, delta, inval_lines, cursor_mode)
1000 } else {
1001 vec![]
1002 }
1003 }
1004 Redo => {
1005 if let Some((text, delta, inval_lines, cursor_mode)) = buffer.do_redo() {
1006 apply_undo_redo(cursor, buffer, modal, text, delta, inval_lines, cursor_mode)
1007 } else {
1008 vec![]
1009 }
1010 }
1011 ClipboardCopy => {
1012 let data = cursor.yank(buffer);
1013 clipboard.put_string(data.content);
1014
1015 match &cursor.mode {
1016 CursorMode::Visual { start, end, .. } => {
1017 let offset = *start.min(end);
1018 let offset = buffer.offset_line_end(offset, false).min(offset);
1019 cursor.mode = CursorMode::Normal {
1020 offset,
1021 affinity: CursorAffinity::Forward,
1022 };
1023 }
1024 CursorMode::Normal { .. } | CursorMode::Insert(_) => {}
1025 }
1026 vec![]
1027 }
1028 ClipboardCut => {
1029 let data = cursor.yank(buffer);
1030 clipboard.put_string(data.content);
1031
1032 let selection = if let CursorMode::Insert(mut selection) = cursor.mode.clone() {
1033 for region in selection.regions_mut() {
1034 if region.is_caret() {
1035 let line = buffer.line_of_offset(region.start);
1036 let start = buffer.offset_of_line(line);
1037 let end = buffer.offset_of_line(line + 1);
1038 region.start = start;
1039 region.end = end;
1040 }
1041 }
1042 selection
1043 } else {
1044 cursor.edit_selection(buffer)
1045 };
1046
1047 let (text, delta, inval_lines) = buffer.edit([(&selection, "")], EditType::Cut);
1048 let selection = selection.apply_delta(&delta, true, InsertDrift::Default);
1049 cursor.update_selection(buffer, selection);
1050 vec![(text, delta, inval_lines)]
1051 }
1052 ClipboardPaste => {
1053 if let Some(s) = clipboard.get_string() {
1054 let mode = if s.ends_with('\n') {
1055 VisualMode::Linewise
1056 } else {
1057 VisualMode::Normal
1058 };
1059 let data = RegisterData { content: s, mode };
1060 Self::do_paste(cursor, buffer, &data)
1061 } else {
1062 vec![]
1063 }
1064 }
1065 Yank => {
1066 match &cursor.mode {
1067 CursorMode::Visual { start, end, .. } => {
1068 let data = cursor.yank(buffer);
1069 register.add_yank(data);
1070
1071 let offset = *start.min(end);
1072 let offset = buffer.offset_line_end(offset, false).min(offset);
1073 cursor.mode = CursorMode::Normal {
1074 offset,
1075 affinity: CursorAffinity::Forward,
1076 };
1077 }
1078 CursorMode::Normal { .. } => {}
1079 CursorMode::Insert(_) => {}
1080 }
1081 vec![]
1082 }
1083 Paste => {
1084 let data = register.unnamed.clone();
1085 Self::do_paste(cursor, buffer, &data)
1086 }
1087 PasteBefore => {
1088 let offset = cursor.offset();
1089 let data = register.unnamed.clone();
1090 let mut local_cursor =
1091 Cursor::new(CursorMode::Insert(Selection::new()), None, None);
1092 local_cursor.set_offset(offset, CursorAffinity::Forward, false, false);
1093 Self::do_paste(&mut local_cursor, buffer, &data)
1094 }
1095 NewLineAbove => {
1096 let offset = cursor.offset();
1097 let line = buffer.line_of_offset(offset);
1098 let offset = if line > 0 {
1099 buffer.line_end_offset(line - 1, true)
1100 } else {
1101 buffer.first_non_blank_character_on_line(line)
1102 };
1103 let delta = Self::insert_new_line(
1104 buffer,
1105 cursor,
1106 Selection::imaginary_caret(offset),
1107 keep_indent,
1108 auto_indent,
1109 );
1110 if line == 0 {
1111 cursor.mode =
1112 CursorMode::Insert(Selection::caret(offset, CursorAffinity::Backward));
1113 }
1114 delta
1115 }
1116 NewLineBelow => {
1117 let offset = cursor.offset();
1118 let offset = buffer.offset_line_end(offset, true);
1119 Self::insert_new_line(
1120 buffer,
1121 cursor,
1122 Selection::imaginary_caret(offset),
1123 keep_indent,
1124 auto_indent,
1125 )
1126 }
1127 DeleteBackward => {
1128 let (selection, edit_type) = match cursor.mode {
1129 CursorMode::Normal { .. } => (cursor.edit_selection(buffer), EditType::Delete),
1130 CursorMode::Visual { .. } => {
1131 (cursor.edit_selection(buffer), EditType::DeleteSelection)
1132 }
1133 CursorMode::Insert(_) => {
1134 let selection = cursor.edit_selection(buffer);
1135 let edit_type = if selection.is_caret() {
1136 EditType::Delete
1137 } else {
1138 EditType::DeleteSelection
1139 };
1140 let indent = buffer.indent_unit();
1141 let mut new_selection = Selection::new();
1142 for region in selection.regions() {
1143 let new_region = if region.is_caret() {
1144 if indent.starts_with('\t') {
1145 let new_end = buffer.move_left(region.end, Mode::Insert, 1);
1146 SelRegion::new(region.start, new_end, region.affinity, None)
1147 } else {
1148 let line = buffer.line_of_offset(region.start);
1149 let nonblank = buffer.first_non_blank_character_on_line(line);
1150 let (_, col) = buffer.offset_to_line_col(region.start);
1151 let count = if region.start <= nonblank && col > 0 {
1152 let r = col % indent.len();
1153 if r == 0 { indent.len() } else { r }
1154 } else {
1155 1
1156 };
1157 let new_end = buffer.move_left(region.end, Mode::Insert, count);
1158 SelRegion::new(region.start, new_end, region.affinity, None)
1159 }
1160 } else {
1161 *region
1162 };
1163 new_selection.add_region(new_region);
1164 }
1165
1166 let mut selection = new_selection;
1167 if selection.regions().len() == 1 {
1168 let delete_str = buffer
1169 .slice_to_cow(selection.min_offset()..selection.max_offset())
1170 .to_string();
1171 if str_is_pair_left(&delete_str)
1172 || delete_str == "\""
1173 || delete_str == "'"
1174 || delete_str == "`"
1175 {
1176 let matching_char = match delete_str.as_str() {
1177 "\"" => Some('"'),
1178 "'" => Some('\''),
1179 "`" => Some('`'),
1180 _ => str_matching_pair(&delete_str),
1181 };
1182 if let Some(c) = matching_char {
1183 let offset = selection.max_offset();
1184 let line = buffer.line_of_offset(offset);
1185 let line_end = buffer.line_end_offset(line, true);
1186 let content = buffer.slice_to_cow(offset..line_end).to_string();
1187 if content.trim().starts_with(&c.to_string()) {
1188 let index = content.match_indices(c).next().unwrap().0;
1189 let affinity = selection.get_cursor_affinity();
1190 selection = Selection::region(
1191 selection.min_offset(),
1192 offset + index + 1,
1193 affinity,
1194 );
1195 }
1196 }
1197 }
1198 }
1199 (selection, edit_type)
1200 }
1201 };
1202 let (text, delta, inval_lines) = buffer.edit([(&selection, "")], edit_type);
1203 let selection = selection.apply_delta(&delta, true, InsertDrift::Default);
1204 cursor.update_selection(buffer, selection);
1205 vec![(text, delta, inval_lines)]
1206 }
1207 DeleteForward => {
1208 let (selection, edit_type) = match cursor.mode {
1209 CursorMode::Normal { .. } => (cursor.edit_selection(buffer), EditType::Delete),
1210 CursorMode::Visual { .. } => {
1211 (cursor.edit_selection(buffer), EditType::DeleteSelection)
1212 }
1213 CursorMode::Insert(_) => {
1214 let selection = cursor.edit_selection(buffer);
1215 let edit_type = if selection.is_caret() {
1216 EditType::Delete
1217 } else {
1218 EditType::DeleteSelection
1219 };
1220 let mut new_selection = Selection::new();
1221 for region in selection.regions() {
1222 let new_region = if region.is_caret() {
1223 let new_end = buffer.move_right(region.end, Mode::Insert, 1);
1224 SelRegion::new(region.start, new_end, region.affinity, None)
1225 } else {
1226 *region
1227 };
1228 new_selection.add_region(new_region);
1229 }
1230 (new_selection, edit_type)
1231 }
1232 };
1233 let (text, delta, inval_lines) = buffer.edit([(&selection, "")], edit_type);
1234 let selection = selection.apply_delta(&delta, true, InsertDrift::Default);
1235 cursor.update_selection(buffer, selection);
1236 vec![(text, delta, inval_lines)]
1237 }
1238 DeleteSelection => {
1239 let selection = match cursor.mode {
1240 CursorMode::Normal { .. } => cursor.edit_selection(buffer),
1241 CursorMode::Visual { .. } => cursor.edit_selection(buffer),
1242 CursorMode::Insert(_) => cursor.edit_selection(buffer),
1243 };
1244
1245 if selection.regions().iter().all(|r| r.is_caret()) {
1246 return vec![];
1247 }
1248
1249 let (text, delta, inval_lines) =
1250 buffer.edit([(&selection, "")], EditType::DeleteSelection);
1251 let selection = selection.apply_delta(&delta, true, InsertDrift::Default);
1252 cursor.update_selection(buffer, selection);
1253 vec![(text, delta, inval_lines)]
1254 }
1255 DeleteLine => {
1256 let selection = cursor.edit_selection(buffer);
1257 let range = format_start_end(
1258 buffer,
1259 selection.min_offset()..selection.max_offset(),
1260 true,
1261 false,
1262 1,
1263 );
1264 let selection = Selection::region(range.start, range.end, CursorAffinity::Forward);
1265 let (text, delta, inval_lines) = buffer.edit([(&selection, "")], EditType::Delete);
1266 let selection = selection.apply_delta(&delta, true, InsertDrift::Default);
1267 cursor.mode = CursorMode::Insert(selection);
1268 vec![(text, delta, inval_lines)]
1269 }
1270 DeleteWordForward => {
1271 let selection = match cursor.mode {
1272 CursorMode::Normal { .. } | CursorMode::Visual { .. } => {
1273 cursor.edit_selection(buffer)
1274 }
1275 CursorMode::Insert(_) => {
1276 let mut new_selection = Selection::new();
1277 let selection = cursor.edit_selection(buffer);
1278
1279 for region in selection.regions() {
1280 let end = buffer.move_word_forward(region.end);
1281 let new_region =
1282 SelRegion::new(region.start, end, region.affinity, None);
1283 new_selection.add_region(new_region);
1284 }
1285
1286 new_selection
1287 }
1288 };
1289 let (text, delta, inval_lines) =
1290 buffer.edit([(&selection, "")], EditType::DeleteWord);
1291 let selection = selection.apply_delta(&delta, true, InsertDrift::Default);
1292 cursor.update_selection(buffer, selection);
1293 vec![(text, delta, inval_lines)]
1294 }
1295 DeleteWordBackward => {
1296 let selection = match cursor.mode {
1297 CursorMode::Normal { .. } | CursorMode::Visual { .. } => {
1298 cursor.edit_selection(buffer)
1299 }
1300 CursorMode::Insert(_) => {
1301 let mut new_selection = Selection::new();
1302 let selection = cursor.edit_selection(buffer);
1303
1304 for region in selection.regions() {
1305 let end = buffer.move_word_backward_deletion(region.end);
1306 let new_region =
1307 SelRegion::new(region.start, end, region.affinity, None);
1308 new_selection.add_region(new_region);
1309 }
1310
1311 new_selection
1312 }
1313 };
1314 let (text, delta, inval_lines) =
1315 buffer.edit([(&selection, "")], EditType::DeleteWord);
1316 let selection = selection.apply_delta(&delta, true, InsertDrift::Default);
1317 cursor.update_selection(buffer, selection);
1318 vec![(text, delta, inval_lines)]
1319 }
1320 DeleteToBeginningOfLine => {
1321 let selection = match cursor.mode {
1322 CursorMode::Normal { .. } | CursorMode::Visual { .. } => {
1323 cursor.edit_selection(buffer)
1324 }
1325 CursorMode::Insert(_) => {
1326 let selection = cursor.edit_selection(buffer);
1327
1328 let mut new_selection = Selection::new();
1329 for region in selection.regions() {
1330 let line = buffer.line_of_offset(region.end);
1331 let end = buffer.offset_of_line(line);
1332 let new_region =
1333 SelRegion::new(region.start, end, region.affinity, None);
1334 new_selection.add_region(new_region);
1335 }
1336
1337 new_selection
1338 }
1339 };
1340 let (text, delta, inval_lines) =
1341 buffer.edit([(&selection, "")], EditType::DeleteToBeginningOfLine);
1342 let selection = selection.apply_delta(&delta, true, InsertDrift::Default);
1343 cursor.update_selection(buffer, selection);
1344 vec![(text, delta, inval_lines)]
1345 }
1346 DeleteToEndOfLine => {
1347 let selection = match cursor.mode {
1348 CursorMode::Normal { .. } | CursorMode::Visual { .. } => {
1349 cursor.edit_selection(buffer)
1350 }
1351 CursorMode::Insert(_) => {
1352 let mut selection = cursor.edit_selection(buffer);
1353
1354 let cursor_offset = cursor.offset();
1355 let line = buffer.line_of_offset(cursor_offset);
1356 let end_of_line_offset = buffer.line_end_offset(line, true);
1357 let new_region = SelRegion::new(
1358 cursor_offset,
1359 end_of_line_offset,
1360 CursorAffinity::Backward,
1361 None,
1362 );
1363 selection.add_region(new_region);
1364
1365 selection
1366 }
1367 };
1368 let (text, delta, inval_lines) =
1369 buffer.edit([(&selection, "")], EditType::DeleteToEndOfLine);
1370 let selection = selection.apply_delta(&delta, true, InsertDrift::Default);
1371 cursor.update_selection(buffer, selection);
1372 vec![(text, delta, inval_lines)]
1373 }
1374 DeleteForwardAndInsert => {
1375 let selection = cursor.edit_selection(buffer);
1376 let (text, delta, inval_lines) = buffer.edit([(&selection, "")], EditType::Delete);
1377 let selection = selection.apply_delta(&delta, true, InsertDrift::Default);
1378 cursor.mode = CursorMode::Insert(selection);
1379 vec![(text, delta, inval_lines)]
1380 }
1381 DeleteWordAndInsert => {
1382 let selection = {
1383 let mut new_selection = Selection::new();
1384 let selection = cursor.edit_selection(buffer);
1385
1386 for region in selection.regions() {
1387 let end = buffer.move_word_forward(region.end);
1388 let new_region =
1389 SelRegion::new(region.start, end, CursorAffinity::Backward, None);
1390 new_selection.add_region(new_region);
1391 }
1392
1393 new_selection
1394 };
1395 let (text, delta, inval_lines) =
1396 buffer.edit([(&selection, "")], EditType::DeleteWord);
1397 let selection = selection.apply_delta(&delta, true, InsertDrift::Default);
1398 cursor.mode = CursorMode::Insert(selection);
1399 vec![(text, delta, inval_lines)]
1400 }
1401 DeleteLineAndInsert => {
1402 let selection = cursor.edit_selection(buffer);
1403 let range = format_start_end(
1404 buffer,
1405 selection.min_offset()..selection.max_offset(),
1406 true,
1407 true,
1408 1,
1409 );
1410 let selection =
1411 Selection::region(range.start, range.end - 1, CursorAffinity::Backward); let (text, delta, inval_lines) = buffer.edit([(&selection, "")], EditType::Delete);
1413 let selection = selection.apply_delta(&delta, true, InsertDrift::Default);
1414 cursor.mode = CursorMode::Insert(selection);
1415 vec![(text, delta, inval_lines)]
1416 }
1417 DeleteToEndOfLineAndInsert => {
1418 let mut selection = cursor.edit_selection(buffer);
1419
1420 let cursor_offset = cursor.offset();
1421 let line = buffer.line_of_offset(cursor_offset);
1422 let end_of_line_offset = buffer.line_end_offset(line, true);
1423
1424 let new_region = SelRegion::new(
1425 cursor_offset,
1426 end_of_line_offset,
1427 CursorAffinity::Backward,
1428 None,
1429 );
1430 selection.add_region(new_region);
1431
1432 let (text, delta, inval_lines) = buffer.edit([(&selection, "")], EditType::Delete);
1433 let selection = selection.apply_delta(&delta, true, InsertDrift::Default);
1434 cursor.mode = CursorMode::Insert(selection);
1435 vec![(text, delta, inval_lines)]
1436 }
1437 NormalMode => {
1438 if !modal {
1439 if let CursorMode::Insert(selection) = &cursor.mode {
1440 match selection.regions().len() {
1441 i if i > 1 => {
1442 if let Some(region) = selection.last_inserted() {
1443 let new_selection = Selection::region(
1444 region.start,
1445 region.end,
1446 region.affinity,
1447 );
1448 cursor.mode = CursorMode::Insert(new_selection);
1449 return vec![];
1450 }
1451 }
1452 1 => {
1453 let region = selection.regions()[0];
1454 if !region.is_caret() {
1455 let new_selection =
1456 Selection::caret(region.end, region.affinity);
1457 cursor.mode = CursorMode::Insert(new_selection);
1458 return vec![];
1459 }
1460 }
1461 _ => (),
1462 }
1463 }
1464
1465 return vec![];
1466 }
1467
1468 let offset = match &cursor.mode {
1469 CursorMode::Insert(selection) => {
1470 let offset = selection.min_offset();
1471 buffer.prev_grapheme_offset(
1472 offset,
1473 1,
1474 buffer.offset_of_line(buffer.line_of_offset(offset)),
1475 )
1476 }
1477 CursorMode::Visual { end, .. } => buffer.offset_line_end(*end, false).min(*end),
1478 CursorMode::Normal { offset, .. } => *offset,
1479 };
1480 let affinity = cursor.affinity();
1481
1482 buffer.reset_edit_type();
1483 cursor.mode = CursorMode::Normal { offset, affinity };
1484 cursor.horiz = None;
1485 vec![]
1486 }
1487 InsertMode => {
1488 cursor.mode =
1489 CursorMode::Insert(Selection::caret(cursor.offset(), cursor.affinity()));
1490 vec![]
1491 }
1492 InsertFirstNonBlank => {
1493 match &cursor.mode {
1494 CursorMode::Normal { offset, .. } => {
1495 let line = buffer.line_of_offset(*offset);
1496 let offset = buffer.first_non_blank_character_on_line(line);
1497 cursor.mode =
1498 CursorMode::Insert(Selection::caret(offset, CursorAffinity::Forward));
1499 }
1500 CursorMode::Visual { .. } => {
1501 let mut selection = Selection::new();
1502 for region in cursor.edit_selection(buffer).regions() {
1503 selection.add_region(SelRegion::caret(region.min(), region.affinity));
1504 }
1505 cursor.mode = CursorMode::Insert(selection);
1506 }
1507 CursorMode::Insert(_) => {}
1508 };
1509 vec![]
1510 }
1511 Append => {
1512 let offset = cursor.offset();
1513 let line = buffer.line_of_offset(offset);
1514 let line_len = buffer.line_len(line);
1515 let count = (line_len > 1 || (buffer.last_line() == line && line_len > 0)) as usize;
1516 let offset = buffer.move_right(cursor.offset(), Mode::Insert, count);
1517 cursor.mode =
1518 CursorMode::Insert(Selection::caret(offset, CursorAffinity::Backward));
1519 vec![]
1520 }
1521 AppendEndOfLine => {
1522 let offset = cursor.offset();
1523 let line = buffer.line_of_offset(offset);
1524 let offset = buffer.line_end_offset(line, true);
1525 cursor.mode =
1526 CursorMode::Insert(Selection::caret(offset, CursorAffinity::Backward));
1527 vec![]
1528 }
1529 ToggleVisualMode => {
1530 Self::toggle_visual(cursor, VisualMode::Normal, modal);
1531 vec![]
1532 }
1533 ToggleLinewiseVisualMode => {
1534 Self::toggle_visual(cursor, VisualMode::Linewise, modal);
1535 vec![]
1536 }
1537 ToggleBlockwiseVisualMode => {
1538 Self::toggle_visual(cursor, VisualMode::Blockwise, modal);
1539 vec![]
1540 }
1541 DuplicateLineUp => Self::duplicate_line(cursor, buffer, DuplicateDirection::Up),
1542 DuplicateLineDown => Self::duplicate_line(cursor, buffer, DuplicateDirection::Down),
1543 NormalizeLineEndings => {
1544 let Some((text, delta, inval)) = buffer.normalize_line_endings() else {
1545 return vec![];
1546 };
1547
1548 cursor.apply_delta(&delta);
1549
1550 vec![(text, delta, inval)]
1551 }
1552 }
1553 }
1554}
1555
1556fn apply_undo_redo(
1557 cursor: &mut Cursor,
1558 buffer: &mut Buffer,
1559 modal: bool,
1560 text: Rope,
1561 delta: RopeDelta,
1562 inval_lines: InvalLines,
1563 cursor_mode: Option<CursorMode>,
1564) -> Vec<(Rope, RopeDelta, InvalLines)> {
1565 if let Some(cursor_mode) = cursor_mode {
1566 cursor.mode = if modal {
1567 CursorMode::Normal {
1568 offset: cursor_mode.offset(),
1569 affinity: cursor_mode.affinity(),
1570 }
1571 } else if cursor.is_insert() {
1572 cursor_mode
1573 } else {
1574 CursorMode::Insert(Selection::caret(
1575 cursor_mode.offset(),
1576 cursor_mode.affinity(),
1577 ))
1578 };
1579 } else if let Some(new_cursor) = get_first_selection_after(cursor, buffer, &delta) {
1580 *cursor = new_cursor
1581 } else if !delta
1582 .els
1583 .iter()
1584 .any(|el| matches!(el, DeltaElement::Copy(_, _)))
1585 {
1586 } else {
1589 cursor.apply_delta(&delta);
1590 }
1591 vec![(text, delta, inval_lines)]
1592}
1593
1594enum DuplicateDirection {
1595 Up,
1596 Down,
1597}
1598
1599#[cfg(test)]
1600mod test {
1601 use crate::{
1602 buffer::{Buffer, rope_text::RopeText},
1603 cursor::{Cursor, CursorAffinity, CursorMode},
1604 editor::{Action, DuplicateDirection},
1605 selection::{SelRegion, Selection},
1606 word::WordCursor,
1607 };
1608
1609 fn prev_unmatched(buffer: &Buffer, c: char, offset: usize) -> Option<usize> {
1610 WordCursor::new(buffer.text(), offset).previous_unmatched(c)
1611 }
1612
1613 #[test]
1614 fn test_insert_simple() {
1615 let mut buffer = Buffer::new("abc");
1616 let mut cursor = Cursor::new(
1617 CursorMode::Insert(Selection::caret(1, CursorAffinity::Backward)),
1618 None,
1619 None,
1620 );
1621
1622 Action::insert(&mut cursor, &mut buffer, "e", &prev_unmatched, true, true);
1623 assert_eq!("aebc", buffer.slice_to_cow(0..buffer.len()));
1624 }
1625
1626 #[test]
1627 fn test_insert_multiple_cursor() {
1628 let mut buffer = Buffer::new("abc\nefg\n");
1629 let mut selection = Selection::new();
1630 selection.add_region(SelRegion::caret(1, CursorAffinity::Backward));
1631 selection.add_region(SelRegion::caret(5, CursorAffinity::Backward));
1632 let mut cursor = Cursor::new(CursorMode::Insert(selection), None, None);
1633
1634 Action::insert(&mut cursor, &mut buffer, "i", &prev_unmatched, true, true);
1635 assert_eq!("aibc\neifg\n", buffer.slice_to_cow(0..buffer.len()));
1636 }
1637
1638 #[test]
1639 fn test_insert_complex() {
1640 let mut buffer = Buffer::new("abc\nefg\n");
1641 let mut selection = Selection::new();
1642 selection.add_region(SelRegion::caret(1, CursorAffinity::Backward));
1643 selection.add_region(SelRegion::caret(5, CursorAffinity::Backward));
1644 let mut cursor = Cursor::new(CursorMode::Insert(selection), None, None);
1645
1646 Action::insert(&mut cursor, &mut buffer, "i", &prev_unmatched, true, true);
1647 assert_eq!("aibc\neifg\n", buffer.slice_to_cow(0..buffer.len()));
1648 Action::insert(&mut cursor, &mut buffer, "j", &prev_unmatched, true, true);
1649 assert_eq!("aijbc\neijfg\n", buffer.slice_to_cow(0..buffer.len()));
1650 Action::insert(&mut cursor, &mut buffer, "{", &prev_unmatched, true, true);
1651 assert_eq!("aij{bc\neij{fg\n", buffer.slice_to_cow(0..buffer.len()));
1652 Action::insert(&mut cursor, &mut buffer, " ", &prev_unmatched, true, true);
1653 assert_eq!("aij{ bc\neij{ fg\n", buffer.slice_to_cow(0..buffer.len()));
1654 }
1655
1656 #[test]
1657 fn test_insert_pair() {
1658 let mut buffer = Buffer::new("a bc\ne fg\n");
1659 let mut selection = Selection::new();
1660 selection.add_region(SelRegion::caret(1, CursorAffinity::Backward));
1661 selection.add_region(SelRegion::caret(6, CursorAffinity::Backward));
1662 let mut cursor = Cursor::new(CursorMode::Insert(selection), None, None);
1663
1664 Action::insert(&mut cursor, &mut buffer, "{", &prev_unmatched, true, true);
1665 assert_eq!("a{} bc\ne{} fg\n", buffer.slice_to_cow(0..buffer.len()));
1666 Action::insert(&mut cursor, &mut buffer, "}", &prev_unmatched, true, true);
1667 assert_eq!("a{} bc\ne{} fg\n", buffer.slice_to_cow(0..buffer.len()));
1668 }
1669
1670 #[test]
1671 fn test_insert_pair_with_selection() {
1672 let mut buffer = Buffer::new("a bc\ne fg\n");
1673 let mut selection = Selection::new();
1674 selection.add_region(SelRegion::new(0, 4, CursorAffinity::Backward, None));
1675 selection.add_region(SelRegion::new(5, 9, CursorAffinity::Backward, None));
1676 let mut cursor = Cursor::new(CursorMode::Insert(selection), None, None);
1677 Action::insert(&mut cursor, &mut buffer, "{", &prev_unmatched, true, true);
1678 assert_eq!("{a bc}\n{e fg}\n", buffer.slice_to_cow(0..buffer.len()));
1679 }
1680
1681 #[test]
1682 fn test_insert_pair_without_auto_closing() {
1683 let mut buffer = Buffer::new("a bc\ne fg\n");
1684 let mut selection = Selection::new();
1685 selection.add_region(SelRegion::caret(1, CursorAffinity::Backward));
1686 selection.add_region(SelRegion::caret(6, CursorAffinity::Backward));
1687 let mut cursor = Cursor::new(CursorMode::Insert(selection), None, None);
1688
1689 Action::insert(&mut cursor, &mut buffer, "{", &prev_unmatched, false, false);
1690 assert_eq!("a{ bc\ne{ fg\n", buffer.slice_to_cow(0..buffer.len()));
1691 Action::insert(&mut cursor, &mut buffer, "}", &prev_unmatched, false, false);
1692 assert_eq!("a{} bc\ne{} fg\n", buffer.slice_to_cow(0..buffer.len()));
1693 }
1694
1695 #[test]
1696 fn duplicate_down_simple() {
1697 let mut buffer = Buffer::new("first line\nsecond line\n");
1698 let mut selection = Selection::new();
1699 selection.add_region(SelRegion::caret(0, CursorAffinity::Backward));
1700 let mut cursor = Cursor::new(CursorMode::Insert(selection), None, None);
1701
1702 Action::duplicate_line(&mut cursor, &mut buffer, DuplicateDirection::Down);
1703
1704 assert_ne!(cursor.offset(), 0);
1705 assert_eq!(
1706 "first line\nfirst line\nsecond line\n",
1707 buffer.slice_to_cow(0..buffer.len())
1708 );
1709 }
1710
1711 #[test]
1712 fn duplicate_up_simple() {
1713 let mut buffer = Buffer::new("first line\nsecond line\n");
1714 let mut selection = Selection::new();
1715 selection.add_region(SelRegion::caret(0, CursorAffinity::Backward));
1716 let mut cursor = Cursor::new(CursorMode::Insert(selection), None, None);
1717
1718 Action::duplicate_line(&mut cursor, &mut buffer, DuplicateDirection::Up);
1719
1720 assert_eq!(cursor.offset(), 0);
1721 assert_eq!(
1722 "first line\nfirst line\nsecond line\n",
1723 buffer.slice_to_cow(0..buffer.len())
1724 );
1725 }
1726
1727 #[test]
1728 fn duplicate_down_multiple_cursors_in_same_line() {
1729 let mut buffer = Buffer::new("first line\nsecond line\n");
1730 let mut selection = Selection::new();
1731 selection.add_region(SelRegion::caret(0, CursorAffinity::Backward));
1732 selection.add_region(SelRegion::caret(1, CursorAffinity::Backward));
1733 let mut cursor = Cursor::new(CursorMode::Insert(selection), None, None);
1734
1735 Action::duplicate_line(&mut cursor, &mut buffer, DuplicateDirection::Down);
1736
1737 assert_eq!(
1738 "first line\nfirst line\nsecond line\n",
1739 buffer.slice_to_cow(0..buffer.len())
1740 );
1741 }
1742
1743 #[test]
1744 fn duplicate_up_multiple_cursors_in_same_line() {
1745 let mut buffer = Buffer::new("first line\nsecond line\n");
1746 let mut selection = Selection::new();
1747 selection.add_region(SelRegion::caret(0, CursorAffinity::Backward));
1748 selection.add_region(SelRegion::caret(1, CursorAffinity::Backward));
1749 let mut cursor = Cursor::new(CursorMode::Insert(selection), None, None);
1750
1751 Action::duplicate_line(&mut cursor, &mut buffer, DuplicateDirection::Up);
1752
1753 assert_eq!(
1754 "first line\nfirst line\nsecond line\n",
1755 buffer.slice_to_cow(0..buffer.len())
1756 );
1757 }
1758
1759 #[test]
1760 fn duplicate_down_multiple() {
1761 let mut buffer = Buffer::new("first line\nsecond line\n");
1762 let mut selection = Selection::new();
1763 selection.add_region(SelRegion::caret(0, CursorAffinity::Backward));
1764 selection.add_region(SelRegion::caret(15, CursorAffinity::Backward));
1765 let mut cursor = Cursor::new(CursorMode::Insert(selection), None, None);
1766
1767 Action::duplicate_line(&mut cursor, &mut buffer, DuplicateDirection::Down);
1768
1769 assert_eq!(
1770 "first line\nfirst line\nsecond line\nsecond line\n",
1771 buffer.slice_to_cow(0..buffer.len())
1772 );
1773 }
1774
1775 #[test]
1776 fn duplicate_up_multiple() {
1777 let mut buffer = Buffer::new("first line\nsecond line\n");
1778 let mut selection = Selection::new();
1779 selection.add_region(SelRegion::caret(0, CursorAffinity::Backward));
1780 selection.add_region(SelRegion::caret(15, CursorAffinity::Backward));
1781 let mut cursor = Cursor::new(CursorMode::Insert(selection), None, None);
1782
1783 Action::duplicate_line(&mut cursor, &mut buffer, DuplicateDirection::Up);
1784
1785 assert_eq!(
1786 "first line\nfirst line\nsecond line\nsecond line\n",
1787 buffer.slice_to_cow(0..buffer.len())
1788 );
1789 }
1790
1791 #[test]
1792 fn duplicate_down_multiple_with_swapped_cursor_order() {
1793 let mut buffer = Buffer::new("first line\nsecond line\n");
1794 let mut selection = Selection::new();
1795 selection.add_region(SelRegion::caret(15, CursorAffinity::Backward));
1796 selection.add_region(SelRegion::caret(0, CursorAffinity::Backward));
1797 let mut cursor = Cursor::new(CursorMode::Insert(selection), None, None);
1798
1799 Action::duplicate_line(&mut cursor, &mut buffer, DuplicateDirection::Down);
1800
1801 assert_eq!(
1802 "first line\nfirst line\nsecond line\nsecond line\n",
1803 buffer.slice_to_cow(0..buffer.len())
1804 );
1805 }
1806
1807 #[test]
1808 fn duplicate_up_multiple_with_swapped_cursor_order() {
1809 let mut buffer = Buffer::new("first line\nsecond line\n");
1810 let mut selection = Selection::new();
1811 selection.add_region(SelRegion::caret(15, CursorAffinity::Backward));
1812 selection.add_region(SelRegion::caret(0, CursorAffinity::Backward));
1813 let mut cursor = Cursor::new(CursorMode::Insert(selection), None, None);
1814
1815 Action::duplicate_line(&mut cursor, &mut buffer, DuplicateDirection::Up);
1816
1817 assert_eq!(
1818 "first line\nfirst line\nsecond line\nsecond line\n",
1819 buffer.slice_to_cow(0..buffer.len())
1820 );
1821 }
1822
1823 #[test]
1824 fn check_multiple_cursor_match_insertion() {
1825 let mut buffer = Buffer::new(" 123 567 9ab def");
1826 let mut selection = Selection::new();
1827 selection.add_region(SelRegion::caret(0, CursorAffinity::Backward));
1828 selection.add_region(SelRegion::caret(4, CursorAffinity::Backward));
1829 selection.add_region(SelRegion::caret(8, CursorAffinity::Backward));
1830 selection.add_region(SelRegion::caret(12, CursorAffinity::Backward));
1831 let mut cursor = Cursor::new(CursorMode::Insert(selection), None, None);
1832
1833 Action::insert(&mut cursor, &mut buffer, "(", &prev_unmatched, true, true);
1834
1835 assert_eq!(
1836 "() 123() 567() 9ab() def",
1837 buffer.slice_to_cow(0..buffer.len())
1838 );
1839
1840 let mut end_selection = Selection::new();
1841 end_selection.add_region(SelRegion::caret(1, CursorAffinity::Backward));
1842 end_selection.add_region(SelRegion::caret(7, CursorAffinity::Backward));
1843 end_selection.add_region(SelRegion::caret(13, CursorAffinity::Backward));
1844 end_selection.add_region(SelRegion::caret(19, CursorAffinity::Backward));
1845 assert_eq!(cursor.mode, CursorMode::Insert(end_selection));
1846 }
1847
1848 }