floem_editor_core/
editor.rs

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