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 == '\'' || 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                                '`' => '`',
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);
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);
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(line_start, offset);
176
177                                    edits.push((current_selection, format!("{line_indent}{c}")));
178                                    continue;
179                                }
180                            }
181                        }
182
183                        if matching_pair_type == Some(true) || c == '"' || c == '\'' || c == '`' {
184                            // Create a late edit to insert the closing pair, if allowed.
185                            let is_whitespace_or_punct = cursor_char
186                                .map(|c| {
187                                    let prop = get_char_property(c);
188                                    prop == CharClassification::Lf
189                                        || prop == CharClassification::Cr
190                                        || prop == CharClassification::Space
191                                        || prop == CharClassification::Punctuation
192                                })
193                                .unwrap_or(true);
194
195                            let should_insert_pair = match c {
196                                '"' | '\'' | '`' => {
197                                    is_whitespace_or_punct
198                                        && prev_cursor_char
199                                            .map(|c| {
200                                                let prop = get_char_property(c);
201                                                prop == CharClassification::Lf
202                                                    || prop == CharClassification::Cr
203                                                    || prop == CharClassification::Space
204                                                    || prop == CharClassification::Punctuation
205                                            })
206                                            .unwrap_or(true)
207                                }
208                                _ => is_whitespace_or_punct,
209                            };
210
211                            if should_insert_pair {
212                                let insert_after = match c {
213                                    '"' => '"',
214                                    '\'' => '\'',
215                                    '`' => '`',
216                                    _ => matching_char(c).unwrap(),
217                                };
218                                edits_after.push((idx, insert_after));
219                            }
220                        };
221                    }
222
223                    let current_selection = Selection::region(region.start, region.end);
224
225                    edits.push((current_selection, c.to_string()));
226                }
227
228                // Apply edits to current selection
229                let edits = edits
230                    .iter()
231                    .map(|(selection, content)| (selection, content.as_str()))
232                    .collect::<Vec<_>>();
233
234                let (text, delta, inval_lines) = buffer.edit(&edits, EditType::InsertChars);
235
236                buffer.set_cursor_before(CursorMode::Insert(selection.clone()));
237
238                // Update selection
239                let mut selection = selection.apply_delta(&delta, true, InsertDrift::Default);
240
241                buffer.set_cursor_after(CursorMode::Insert(selection.clone()));
242
243                deltas.push((text, delta, inval_lines));
244                // Apply late edits
245                let edits_after = edits_after
246                    .iter()
247                    .map(|(idx, content)| {
248                        let region = &selection.regions()[*idx];
249                        (
250                            Selection::region(region.max(), region.max()),
251                            content.to_string(),
252                        )
253                    })
254                    .collect::<Vec<_>>();
255
256                let edits_after = edits_after
257                    .iter()
258                    .map(|(selection, content)| (selection, content.as_str()))
259                    .collect::<Vec<_>>();
260
261                if !edits_after.is_empty() {
262                    let (text, delta, inval_lines) =
263                        buffer.edit(&edits_after, EditType::InsertChars);
264                    deltas.push((text, delta, inval_lines));
265                }
266
267                // Adjust selection according to previous late edits
268                let mut adjustment = 0;
269                for region in selection
270                    .regions_mut()
271                    .iter_mut()
272                    .sorted_by(|region_a, region_b| region_a.start.cmp(&region_b.start))
273                {
274                    let new_region =
275                        SelRegion::new(region.start + adjustment, region.end + adjustment, None);
276
277                    if let Some(inserted) = edits_after.iter().find_map(|(selection, str)| {
278                        if selection.last_inserted().map(|r| r.start) == Some(region.start) {
279                            Some(str)
280                        } else {
281                            None
282                        }
283                    }) {
284                        adjustment += inserted.len();
285                    }
286
287                    *region = new_region;
288                }
289
290                cursor.mode = CursorMode::Insert(selection);
291            }
292        }
293        deltas
294    }
295
296    fn toggle_visual(cursor: &mut Cursor, visual_mode: VisualMode, modal: bool) {
297        if !modal {
298            return;
299        }
300
301        match &cursor.mode {
302            CursorMode::Visual { start, end, mode } => {
303                if mode != &visual_mode {
304                    cursor.mode = CursorMode::Visual {
305                        start: *start,
306                        end: *end,
307                        mode: visual_mode,
308                    };
309                } else {
310                    cursor.mode = CursorMode::Normal(*end);
311                };
312            }
313            _ => {
314                let offset = cursor.offset();
315                cursor.mode = CursorMode::Visual {
316                    start: offset,
317                    end: offset,
318                    mode: visual_mode,
319                };
320            }
321        }
322    }
323
324    fn insert_new_line(
325        buffer: &mut Buffer,
326        cursor: &mut Cursor,
327        selection: Selection,
328        keep_indent: bool,
329        auto_indent: bool,
330    ) -> Vec<(Rope, RopeDelta, InvalLines)> {
331        let mut edits = Vec::with_capacity(selection.regions().len());
332        let mut extra_edits = Vec::new();
333        let mut shift = 0i32;
334        let line_ending = buffer.line_ending().get_chars();
335        for region in selection.regions() {
336            let offset = region.max();
337            let line = buffer.line_of_offset(offset);
338            let line_start = buffer.offset_of_line(line);
339            let line_end = buffer.line_end_offset(line, true);
340            let line_indent = buffer.indent_on_line(line);
341            let first_half = buffer.slice_to_cow(line_start..offset);
342            let second_half = buffer.slice_to_cow(offset..line_end);
343            let second_half_trim = second_half.trim();
344
345            // TODO: this could be done with 1 string
346            let new_line_content = {
347                let indent_storage;
348                let indent = if auto_indent && has_unmatched_pair(&first_half) {
349                    indent_storage = format!("{}{}", line_indent, buffer.indent_unit());
350                    &indent_storage
351                } else if keep_indent
352                    && (second_half_trim.is_empty() || !second_half.starts_with(&line_indent))
353                {
354                    &line_indent
355                } else {
356                    indent_storage = String::new();
357                    &indent_storage
358                };
359                format!("{line_ending}{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_trim.starts_with(c) {
373                            let selection =
374                                Selection::caret((region.max() as i32 + shift) as usize);
375                            let content = format!("{line_ending}{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                                || delete_str == "`"
1145                            {
1146                                let matching_char = match delete_str.as_str() {
1147                                    "\"" => Some('"'),
1148                                    "'" => Some('\''),
1149                                    "`" => Some('`'),
1150                                    _ => str_matching_pair(&delete_str),
1151                                };
1152                                if let Some(c) = matching_char {
1153                                    let offset = selection.max_offset();
1154                                    let line = buffer.line_of_offset(offset);
1155                                    let line_end = buffer.line_end_offset(line, true);
1156                                    let content = buffer.slice_to_cow(offset..line_end).to_string();
1157                                    if content.trim().starts_with(&c.to_string()) {
1158                                        let index = content.match_indices(c).next().unwrap().0;
1159                                        selection = Selection::region(
1160                                            selection.min_offset(),
1161                                            offset + index + 1,
1162                                        );
1163                                    }
1164                                }
1165                            }
1166                        }
1167                        (selection, edit_type)
1168                    }
1169                };
1170                let (text, delta, inval_lines) = buffer.edit([(&selection, "")], edit_type);
1171                let selection = selection.apply_delta(&delta, true, InsertDrift::Default);
1172                cursor.update_selection(buffer, selection);
1173                vec![(text, delta, inval_lines)]
1174            }
1175            DeleteForward => {
1176                let (selection, edit_type) = match cursor.mode {
1177                    CursorMode::Normal(_) => (cursor.edit_selection(buffer), EditType::Delete),
1178                    CursorMode::Visual { .. } => {
1179                        (cursor.edit_selection(buffer), EditType::DeleteSelection)
1180                    }
1181                    CursorMode::Insert(_) => {
1182                        let selection = cursor.edit_selection(buffer);
1183                        let edit_type = if selection.is_caret() {
1184                            EditType::Delete
1185                        } else {
1186                            EditType::DeleteSelection
1187                        };
1188                        let mut new_selection = Selection::new();
1189                        for region in selection.regions() {
1190                            let new_region = if region.is_caret() {
1191                                let new_end = buffer.move_right(region.end, Mode::Insert, 1);
1192                                SelRegion::new(region.start, new_end, None)
1193                            } else {
1194                                *region
1195                            };
1196                            new_selection.add_region(new_region);
1197                        }
1198                        (new_selection, edit_type)
1199                    }
1200                };
1201                let (text, delta, inval_lines) = buffer.edit([(&selection, "")], edit_type);
1202                let selection = selection.apply_delta(&delta, true, InsertDrift::Default);
1203                cursor.update_selection(buffer, selection);
1204                vec![(text, delta, inval_lines)]
1205            }
1206            DeleteLine => {
1207                let selection = cursor.edit_selection(buffer);
1208                let range = format_start_end(
1209                    buffer,
1210                    selection.min_offset()..selection.max_offset(),
1211                    true,
1212                    false,
1213                    1,
1214                );
1215                let selection = Selection::region(range.start, range.end);
1216                let (text, delta, inval_lines) = buffer.edit([(&selection, "")], EditType::Delete);
1217                let selection = selection.apply_delta(&delta, true, InsertDrift::Default);
1218                cursor.mode = CursorMode::Insert(selection);
1219                vec![(text, delta, inval_lines)]
1220            }
1221            DeleteWordForward => {
1222                let selection = match cursor.mode {
1223                    CursorMode::Normal(_) | CursorMode::Visual { .. } => {
1224                        cursor.edit_selection(buffer)
1225                    }
1226                    CursorMode::Insert(_) => {
1227                        let mut new_selection = Selection::new();
1228                        let selection = cursor.edit_selection(buffer);
1229
1230                        for region in selection.regions() {
1231                            let end = buffer.move_word_forward(region.end);
1232                            let new_region = SelRegion::new(region.start, end, None);
1233                            new_selection.add_region(new_region);
1234                        }
1235
1236                        new_selection
1237                    }
1238                };
1239                let (text, delta, inval_lines) =
1240                    buffer.edit([(&selection, "")], EditType::DeleteWord);
1241                let selection = selection.apply_delta(&delta, true, InsertDrift::Default);
1242                cursor.update_selection(buffer, selection);
1243                vec![(text, delta, inval_lines)]
1244            }
1245            DeleteWordBackward => {
1246                let selection = match cursor.mode {
1247                    CursorMode::Normal(_) | CursorMode::Visual { .. } => {
1248                        cursor.edit_selection(buffer)
1249                    }
1250                    CursorMode::Insert(_) => {
1251                        let mut new_selection = Selection::new();
1252                        let selection = cursor.edit_selection(buffer);
1253
1254                        for region in selection.regions() {
1255                            let end = buffer.move_word_backward_deletion(region.end);
1256                            let new_region = SelRegion::new(region.start, end, None);
1257                            new_selection.add_region(new_region);
1258                        }
1259
1260                        new_selection
1261                    }
1262                };
1263                let (text, delta, inval_lines) =
1264                    buffer.edit([(&selection, "")], EditType::DeleteWord);
1265                let selection = selection.apply_delta(&delta, true, InsertDrift::Default);
1266                cursor.update_selection(buffer, selection);
1267                vec![(text, delta, inval_lines)]
1268            }
1269            DeleteToBeginningOfLine => {
1270                let selection = match cursor.mode {
1271                    CursorMode::Normal(_) | CursorMode::Visual { .. } => {
1272                        cursor.edit_selection(buffer)
1273                    }
1274                    CursorMode::Insert(_) => {
1275                        let selection = cursor.edit_selection(buffer);
1276
1277                        let mut new_selection = Selection::new();
1278                        for region in selection.regions() {
1279                            let line = buffer.line_of_offset(region.end);
1280                            let end = buffer.offset_of_line(line);
1281                            let new_region = SelRegion::new(region.start, end, None);
1282                            new_selection.add_region(new_region);
1283                        }
1284
1285                        new_selection
1286                    }
1287                };
1288                let (text, delta, inval_lines) =
1289                    buffer.edit([(&selection, "")], EditType::DeleteToBeginningOfLine);
1290                let selection = selection.apply_delta(&delta, true, InsertDrift::Default);
1291                cursor.update_selection(buffer, selection);
1292                vec![(text, delta, inval_lines)]
1293            }
1294            DeleteToEndOfLine => {
1295                let selection = match cursor.mode {
1296                    CursorMode::Normal(_) | CursorMode::Visual { .. } => {
1297                        cursor.edit_selection(buffer)
1298                    }
1299                    CursorMode::Insert(_) => {
1300                        let mut selection = cursor.edit_selection(buffer);
1301
1302                        let cursor_offset = cursor.offset();
1303                        let line = buffer.line_of_offset(cursor_offset);
1304                        let end_of_line_offset = buffer.line_end_offset(line, true);
1305                        let new_region = SelRegion::new(cursor_offset, end_of_line_offset, None);
1306                        selection.add_region(new_region);
1307
1308                        selection
1309                    }
1310                };
1311                let (text, delta, inval_lines) =
1312                    buffer.edit([(&selection, "")], EditType::DeleteToEndOfLine);
1313                let selection = selection.apply_delta(&delta, true, InsertDrift::Default);
1314                cursor.update_selection(buffer, selection);
1315                vec![(text, delta, inval_lines)]
1316            }
1317            DeleteForwardAndInsert => {
1318                let selection = cursor.edit_selection(buffer);
1319                let (text, delta, inval_lines) = buffer.edit([(&selection, "")], EditType::Delete);
1320                let selection = selection.apply_delta(&delta, true, InsertDrift::Default);
1321                cursor.mode = CursorMode::Insert(selection);
1322                vec![(text, delta, inval_lines)]
1323            }
1324            DeleteWordAndInsert => {
1325                let selection = {
1326                    let mut new_selection = Selection::new();
1327                    let selection = cursor.edit_selection(buffer);
1328
1329                    for region in selection.regions() {
1330                        let end = buffer.move_word_forward(region.end);
1331                        let new_region = SelRegion::new(region.start, end, None);
1332                        new_selection.add_region(new_region);
1333                    }
1334
1335                    new_selection
1336                };
1337                let (text, delta, inval_lines) =
1338                    buffer.edit([(&selection, "")], EditType::DeleteWord);
1339                let selection = selection.apply_delta(&delta, true, InsertDrift::Default);
1340                cursor.mode = CursorMode::Insert(selection);
1341                vec![(text, delta, inval_lines)]
1342            }
1343            DeleteLineAndInsert => {
1344                let selection = cursor.edit_selection(buffer);
1345                let range = format_start_end(
1346                    buffer,
1347                    selection.min_offset()..selection.max_offset(),
1348                    true,
1349                    true,
1350                    1,
1351                );
1352                let selection = Selection::region(range.start, range.end - 1); // -1 because we want to keep the line itself
1353                let (text, delta, inval_lines) = buffer.edit([(&selection, "")], EditType::Delete);
1354                let selection = selection.apply_delta(&delta, true, InsertDrift::Default);
1355                cursor.mode = CursorMode::Insert(selection);
1356                vec![(text, delta, inval_lines)]
1357            }
1358            DeleteToEndOfLineAndInsert => {
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
1365                let new_region = SelRegion::new(cursor_offset, end_of_line_offset, None);
1366                selection.add_region(new_region);
1367
1368                let (text, delta, inval_lines) = buffer.edit([(&selection, "")], EditType::Delete);
1369                let selection = selection.apply_delta(&delta, true, InsertDrift::Default);
1370                cursor.mode = CursorMode::Insert(selection);
1371                vec![(text, delta, inval_lines)]
1372            }
1373            NormalMode => {
1374                if !modal {
1375                    if let CursorMode::Insert(selection) = &cursor.mode {
1376                        match selection.regions().len() {
1377                            i if i > 1 => {
1378                                if let Some(region) = selection.last_inserted() {
1379                                    let new_selection = Selection::region(region.start, region.end);
1380                                    cursor.mode = CursorMode::Insert(new_selection);
1381                                    return vec![];
1382                                }
1383                            }
1384                            1 => {
1385                                let region = selection.regions()[0];
1386                                if !region.is_caret() {
1387                                    let new_selection = Selection::caret(region.end);
1388                                    cursor.mode = CursorMode::Insert(new_selection);
1389                                    return vec![];
1390                                }
1391                            }
1392                            _ => (),
1393                        }
1394                    }
1395
1396                    return vec![];
1397                }
1398
1399                let offset = match &cursor.mode {
1400                    CursorMode::Insert(selection) => {
1401                        let offset = selection.min_offset();
1402                        buffer.prev_grapheme_offset(
1403                            offset,
1404                            1,
1405                            buffer.offset_of_line(buffer.line_of_offset(offset)),
1406                        )
1407                    }
1408                    CursorMode::Visual { end, .. } => buffer.offset_line_end(*end, false).min(*end),
1409                    CursorMode::Normal(offset) => *offset,
1410                };
1411
1412                buffer.reset_edit_type();
1413                cursor.mode = CursorMode::Normal(offset);
1414                cursor.horiz = None;
1415                vec![]
1416            }
1417            InsertMode => {
1418                cursor.mode = CursorMode::Insert(Selection::caret(cursor.offset()));
1419                vec![]
1420            }
1421            InsertFirstNonBlank => {
1422                match &cursor.mode {
1423                    CursorMode::Normal(offset) => {
1424                        let line = buffer.line_of_offset(*offset);
1425                        let offset = buffer.first_non_blank_character_on_line(line);
1426                        cursor.mode = CursorMode::Insert(Selection::caret(offset));
1427                    }
1428                    CursorMode::Visual { .. } => {
1429                        let mut selection = Selection::new();
1430                        for region in cursor.edit_selection(buffer).regions() {
1431                            selection.add_region(SelRegion::caret(region.min()));
1432                        }
1433                        cursor.mode = CursorMode::Insert(selection);
1434                    }
1435                    CursorMode::Insert(_) => {}
1436                };
1437                vec![]
1438            }
1439            Append => {
1440                let offset = cursor.offset();
1441                let line = buffer.line_of_offset(offset);
1442                let line_len = buffer.line_len(line);
1443                let count = (line_len > 1 || (buffer.last_line() == line && line_len > 0)) as usize;
1444                let offset = buffer.move_right(cursor.offset(), Mode::Insert, count);
1445                cursor.mode = CursorMode::Insert(Selection::caret(offset));
1446                vec![]
1447            }
1448            AppendEndOfLine => {
1449                let offset = cursor.offset();
1450                let line = buffer.line_of_offset(offset);
1451                let offset = buffer.line_end_offset(line, true);
1452                cursor.mode = CursorMode::Insert(Selection::caret(offset));
1453                vec![]
1454            }
1455            ToggleVisualMode => {
1456                Self::toggle_visual(cursor, VisualMode::Normal, modal);
1457                vec![]
1458            }
1459            ToggleLinewiseVisualMode => {
1460                Self::toggle_visual(cursor, VisualMode::Linewise, modal);
1461                vec![]
1462            }
1463            ToggleBlockwiseVisualMode => {
1464                Self::toggle_visual(cursor, VisualMode::Blockwise, modal);
1465                vec![]
1466            }
1467            DuplicateLineUp => Self::duplicate_line(cursor, buffer, DuplicateDirection::Up),
1468            DuplicateLineDown => Self::duplicate_line(cursor, buffer, DuplicateDirection::Down),
1469            NormalizeLineEndings => {
1470                let Some((text, delta, inval)) = buffer.normalize_line_endings() else {
1471                    return vec![];
1472                };
1473
1474                cursor.apply_delta(&delta);
1475
1476                vec![(text, delta, inval)]
1477            }
1478        }
1479    }
1480}
1481
1482fn apply_undo_redo(
1483    cursor: &mut Cursor,
1484    buffer: &mut Buffer,
1485    modal: bool,
1486    text: Rope,
1487    delta: RopeDelta,
1488    inval_lines: InvalLines,
1489    cursor_mode: Option<CursorMode>,
1490) -> Vec<(Rope, RopeDelta, InvalLines)> {
1491    if let Some(cursor_mode) = cursor_mode {
1492        cursor.mode = if modal {
1493            CursorMode::Normal(cursor_mode.offset())
1494        } else if cursor.is_insert() {
1495            cursor_mode
1496        } else {
1497            CursorMode::Insert(Selection::caret(cursor_mode.offset()))
1498        };
1499    } else if let Some(new_cursor) = get_first_selection_after(cursor, buffer, &delta) {
1500        *cursor = new_cursor
1501    } else if !delta
1502        .els
1503        .iter()
1504        .any(|el| matches!(el, DeltaElement::Copy(_, _)))
1505    {
1506        // if there's no copy that means the whole document was swapped
1507        // we'd better not moving the cursor
1508    } else {
1509        cursor.apply_delta(&delta);
1510    }
1511    vec![(text, delta, inval_lines)]
1512}
1513
1514enum DuplicateDirection {
1515    Up,
1516    Down,
1517}
1518
1519#[cfg(test)]
1520mod test {
1521    use crate::{
1522        buffer::{rope_text::RopeText, Buffer},
1523        cursor::{Cursor, CursorMode},
1524        editor::{Action, DuplicateDirection},
1525        selection::{SelRegion, Selection},
1526        word::WordCursor,
1527    };
1528
1529    fn prev_unmatched(buffer: &Buffer, c: char, offset: usize) -> Option<usize> {
1530        WordCursor::new(buffer.text(), offset).previous_unmatched(c)
1531    }
1532
1533    #[test]
1534    fn test_insert_simple() {
1535        let mut buffer = Buffer::new("abc");
1536        let mut cursor = Cursor::new(CursorMode::Insert(Selection::caret(1)), None, None);
1537
1538        Action::insert(&mut cursor, &mut buffer, "e", &prev_unmatched, true, true);
1539        assert_eq!("aebc", buffer.slice_to_cow(0..buffer.len()));
1540    }
1541
1542    #[test]
1543    fn test_insert_multiple_cursor() {
1544        let mut buffer = Buffer::new("abc\nefg\n");
1545        let mut selection = Selection::new();
1546        selection.add_region(SelRegion::caret(1));
1547        selection.add_region(SelRegion::caret(5));
1548        let mut cursor = Cursor::new(CursorMode::Insert(selection), None, None);
1549
1550        Action::insert(&mut cursor, &mut buffer, "i", &prev_unmatched, true, true);
1551        assert_eq!("aibc\neifg\n", buffer.slice_to_cow(0..buffer.len()));
1552    }
1553
1554    #[test]
1555    fn test_insert_complex() {
1556        let mut buffer = Buffer::new("abc\nefg\n");
1557        let mut selection = Selection::new();
1558        selection.add_region(SelRegion::caret(1));
1559        selection.add_region(SelRegion::caret(5));
1560        let mut cursor = Cursor::new(CursorMode::Insert(selection), None, None);
1561
1562        Action::insert(&mut cursor, &mut buffer, "i", &prev_unmatched, true, true);
1563        assert_eq!("aibc\neifg\n", buffer.slice_to_cow(0..buffer.len()));
1564        Action::insert(&mut cursor, &mut buffer, "j", &prev_unmatched, true, true);
1565        assert_eq!("aijbc\neijfg\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        Action::insert(&mut cursor, &mut buffer, " ", &prev_unmatched, true, true);
1569        assert_eq!("aij{ bc\neij{ fg\n", buffer.slice_to_cow(0..buffer.len()));
1570    }
1571
1572    #[test]
1573    fn test_insert_pair() {
1574        let mut buffer = Buffer::new("a bc\ne fg\n");
1575        let mut selection = Selection::new();
1576        selection.add_region(SelRegion::caret(1));
1577        selection.add_region(SelRegion::caret(6));
1578        let mut cursor = Cursor::new(CursorMode::Insert(selection), None, None);
1579
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        Action::insert(&mut cursor, &mut buffer, "}", &prev_unmatched, true, true);
1583        assert_eq!("a{} bc\ne{} fg\n", buffer.slice_to_cow(0..buffer.len()));
1584    }
1585
1586    #[test]
1587    fn test_insert_pair_with_selection() {
1588        let mut buffer = Buffer::new("a bc\ne fg\n");
1589        let mut selection = Selection::new();
1590        selection.add_region(SelRegion::new(0, 4, None));
1591        selection.add_region(SelRegion::new(5, 9, None));
1592        let mut cursor = Cursor::new(CursorMode::Insert(selection), None, None);
1593        Action::insert(&mut cursor, &mut buffer, "{", &prev_unmatched, true, true);
1594        assert_eq!("{a bc}\n{e fg}\n", buffer.slice_to_cow(0..buffer.len()));
1595    }
1596
1597    #[test]
1598    fn test_insert_pair_without_auto_closing() {
1599        let mut buffer = Buffer::new("a bc\ne fg\n");
1600        let mut selection = Selection::new();
1601        selection.add_region(SelRegion::caret(1));
1602        selection.add_region(SelRegion::caret(6));
1603        let mut cursor = Cursor::new(CursorMode::Insert(selection), None, None);
1604
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        Action::insert(&mut cursor, &mut buffer, "}", &prev_unmatched, false, false);
1608        assert_eq!("a{} bc\ne{} fg\n", buffer.slice_to_cow(0..buffer.len()));
1609    }
1610
1611    #[test]
1612    fn duplicate_down_simple() {
1613        let mut buffer = Buffer::new("first line\nsecond line\n");
1614        let mut selection = Selection::new();
1615        selection.add_region(SelRegion::caret(0));
1616        let mut cursor = Cursor::new(CursorMode::Insert(selection), None, None);
1617
1618        Action::duplicate_line(&mut cursor, &mut buffer, DuplicateDirection::Down);
1619
1620        assert_ne!(cursor.offset(), 0);
1621        assert_eq!(
1622            "first line\nfirst line\nsecond line\n",
1623            buffer.slice_to_cow(0..buffer.len())
1624        );
1625    }
1626
1627    #[test]
1628    fn duplicate_up_simple() {
1629        let mut buffer = Buffer::new("first line\nsecond line\n");
1630        let mut selection = Selection::new();
1631        selection.add_region(SelRegion::caret(0));
1632        let mut cursor = Cursor::new(CursorMode::Insert(selection), None, None);
1633
1634        Action::duplicate_line(&mut cursor, &mut buffer, DuplicateDirection::Up);
1635
1636        assert_eq!(cursor.offset(), 0);
1637        assert_eq!(
1638            "first line\nfirst line\nsecond line\n",
1639            buffer.slice_to_cow(0..buffer.len())
1640        );
1641    }
1642
1643    #[test]
1644    fn duplicate_down_multiple_cursors_in_same_line() {
1645        let mut buffer = Buffer::new("first line\nsecond line\n");
1646        let mut selection = Selection::new();
1647        selection.add_region(SelRegion::caret(0));
1648        selection.add_region(SelRegion::caret(1));
1649        let mut cursor = Cursor::new(CursorMode::Insert(selection), None, None);
1650
1651        Action::duplicate_line(&mut cursor, &mut buffer, DuplicateDirection::Down);
1652
1653        assert_eq!(
1654            "first line\nfirst line\nsecond line\n",
1655            buffer.slice_to_cow(0..buffer.len())
1656        );
1657    }
1658
1659    #[test]
1660    fn duplicate_up_multiple_cursors_in_same_line() {
1661        let mut buffer = Buffer::new("first line\nsecond line\n");
1662        let mut selection = Selection::new();
1663        selection.add_region(SelRegion::caret(0));
1664        selection.add_region(SelRegion::caret(1));
1665        let mut cursor = Cursor::new(CursorMode::Insert(selection), None, None);
1666
1667        Action::duplicate_line(&mut cursor, &mut buffer, DuplicateDirection::Up);
1668
1669        assert_eq!(
1670            "first line\nfirst line\nsecond line\n",
1671            buffer.slice_to_cow(0..buffer.len())
1672        );
1673    }
1674
1675    #[test]
1676    fn duplicate_down_multiple() {
1677        let mut buffer = Buffer::new("first line\nsecond line\n");
1678        let mut selection = Selection::new();
1679        selection.add_region(SelRegion::caret(0));
1680        selection.add_region(SelRegion::caret(15));
1681        let mut cursor = Cursor::new(CursorMode::Insert(selection), None, None);
1682
1683        Action::duplicate_line(&mut cursor, &mut buffer, DuplicateDirection::Down);
1684
1685        assert_eq!(
1686            "first line\nfirst line\nsecond line\nsecond line\n",
1687            buffer.slice_to_cow(0..buffer.len())
1688        );
1689    }
1690
1691    #[test]
1692    fn duplicate_up_multiple() {
1693        let mut buffer = Buffer::new("first line\nsecond line\n");
1694        let mut selection = Selection::new();
1695        selection.add_region(SelRegion::caret(0));
1696        selection.add_region(SelRegion::caret(15));
1697        let mut cursor = Cursor::new(CursorMode::Insert(selection), None, None);
1698
1699        Action::duplicate_line(&mut cursor, &mut buffer, DuplicateDirection::Up);
1700
1701        assert_eq!(
1702            "first line\nfirst line\nsecond line\nsecond line\n",
1703            buffer.slice_to_cow(0..buffer.len())
1704        );
1705    }
1706
1707    #[test]
1708    fn duplicate_down_multiple_with_swapped_cursor_order() {
1709        let mut buffer = Buffer::new("first line\nsecond line\n");
1710        let mut selection = Selection::new();
1711        selection.add_region(SelRegion::caret(15));
1712        selection.add_region(SelRegion::caret(0));
1713        let mut cursor = Cursor::new(CursorMode::Insert(selection), None, None);
1714
1715        Action::duplicate_line(&mut cursor, &mut buffer, DuplicateDirection::Down);
1716
1717        assert_eq!(
1718            "first line\nfirst line\nsecond line\nsecond line\n",
1719            buffer.slice_to_cow(0..buffer.len())
1720        );
1721    }
1722
1723    #[test]
1724    fn duplicate_up_multiple_with_swapped_cursor_order() {
1725        let mut buffer = Buffer::new("first line\nsecond line\n");
1726        let mut selection = Selection::new();
1727        selection.add_region(SelRegion::caret(15));
1728        selection.add_region(SelRegion::caret(0));
1729        let mut cursor = Cursor::new(CursorMode::Insert(selection), None, None);
1730
1731        Action::duplicate_line(&mut cursor, &mut buffer, DuplicateDirection::Up);
1732
1733        assert_eq!(
1734            "first line\nfirst line\nsecond line\nsecond line\n",
1735            buffer.slice_to_cow(0..buffer.len())
1736        );
1737    }
1738
1739    #[test]
1740    fn check_multiple_cursor_match_insertion() {
1741        let mut buffer = Buffer::new(" 123 567 9ab def");
1742        let mut selection = Selection::new();
1743        selection.add_region(SelRegion::caret(0));
1744        selection.add_region(SelRegion::caret(4));
1745        selection.add_region(SelRegion::caret(8));
1746        selection.add_region(SelRegion::caret(12));
1747        let mut cursor = Cursor::new(CursorMode::Insert(selection), None, None);
1748
1749        Action::insert(&mut cursor, &mut buffer, "(", &prev_unmatched, true, true);
1750
1751        assert_eq!(
1752            "() 123() 567() 9ab() def",
1753            buffer.slice_to_cow(0..buffer.len())
1754        );
1755
1756        let mut end_selection = Selection::new();
1757        end_selection.add_region(SelRegion::caret(1));
1758        end_selection.add_region(SelRegion::caret(7));
1759        end_selection.add_region(SelRegion::caret(13));
1760        end_selection.add_region(SelRegion::caret(19));
1761        assert_eq!(cursor.mode, CursorMode::Insert(end_selection));
1762    }
1763
1764    // TODO(dbuga): add tests duplicating selections (multiple line blocks)
1765}