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