Skip to main content

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