Skip to main content

indigo_core/mode/
normal.rs

1#![allow(clippy::enum_glob_use)]
2
3use crate::{
4    editor::Editor,
5    keymap::{Keymap, KeymapResult, keymap},
6    mode::{
7        Mode,
8        command::enter_command_mode,
9        insert, prompt, replace,
10        seek::{self, SeekDirection, SeekInclude, SeekSelect},
11    },
12    rope::{LINE_TYPE, RopeExt as _},
13    selection::PasteMode,
14    window::{
15        scroll_full_page_down, scroll_full_page_up, scroll_half_page_down, scroll_half_page_up,
16    },
17};
18use regex_cursor::engines::meta::Regex;
19use std::{cmp::min, num::NonZeroUsize, sync::LazyLock};
20
21#[cfg(feature = "arbitrary")]
22use arbitrary::Arbitrary;
23
24#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
25#[derive(Debug)]
26pub enum Action {
27    AppendCountDigit(u8),
28    ClearCount,
29    Commit,
30    EnterNormalMode,
31    EnterCommandMode,
32    EnterInsertMode,
33    EnterSeekMode {
34        select: SeekSelect,
35        include: SeekInclude,
36        direction: SeekDirection,
37    },
38    EnterReplaceMode,
39    InsertAfterHead,
40    InsertAtLineNonBlankStart,
41    InsertAtLineEnd,
42    InsertLineAbove,
43    InsertLineBelow,
44    AddLineAbove,
45    AddLineBelow,
46    MoveLeft,
47    MoveRight,
48    MoveUp,
49    MoveDown,
50    ExtendLeft,
51    ExtendRight,
52    ExtendUp,
53    ExtendDown,
54    GotoMoveToLine,
55    GotoMoveToStart,
56    GotoExtendToStart,
57    GotoMoveToBottom,
58    GotoExtendToBottom,
59    GotoMoveToEnd,
60    GotoExtendToEnd,
61    GotoMoveToLineStart,
62    GotoExtendToLineStart,
63    GotoMoveToLineNonBlankStart,
64    GotoExtendToLineNonBlankStart,
65    GotoMoveUntilLineEnd,
66    GotoExtendUntilLineEnd,
67    KeepPrimary,
68    RotateSelectionBackward,
69    RotateSelectionForward,
70    Collapse,
71    Flip,
72    FlipForward,
73    SelectAll,
74    ExpandToFullLines,
75    SplitIntoLines,
76    SelectRegex,
77    Delete,
78    Yank,
79    PasteAfter,
80    PasteBefore,
81    PasteReplace,
82    PasteAllAfter,
83    PasteAllBefore,
84    PasteAllReplace,
85    Undo,
86    Redo,
87    ScrollHalfPageUp,
88    ScrollHalfPageDown,
89    ScrollFullPageUp,
90    ScrollFullPageDown,
91}
92
93pub static KEYMAP: LazyLock<Keymap<Vec<Action>>> = LazyLock::new(|| {
94    use Action::*;
95    use SeekDirection::{Next, Prev};
96    use SeekInclude::{Onto, Until};
97    use SeekSelect::{Extend, Move};
98    keymap! { Vec<Action>;
99        "<esc>" => vec![EnterNormalMode],
100
101        "0" => vec![AppendCountDigit(0)],
102        "1" => vec![AppendCountDigit(1)],
103        "2" => vec![AppendCountDigit(2)],
104        "3" => vec![AppendCountDigit(3)],
105        "4" => vec![AppendCountDigit(4)],
106        "5" => vec![AppendCountDigit(5)],
107        "6" => vec![AppendCountDigit(6)],
108        "7" => vec![AppendCountDigit(7)],
109        "8" => vec![AppendCountDigit(8)],
110        "9" => vec![AppendCountDigit(9)],
111
112        "h" => vec![MoveLeft], /// Move left
113        "l" => vec![MoveRight], /// Move right
114        "k" => vec![MoveUp], /// Move up
115        "j" => vec![MoveDown], /// Move down
116        "H" => vec![ExtendLeft], /// Extend left
117        "L" => vec![ExtendRight], /// Extend right
118        "K" => vec![ExtendUp], /// Extend up
119        "J" => vec![ExtendDown], /// Extend down
120
121        "i" => vec![EnterInsertMode], /// Enter insert mode at cursor
122        "a" => vec![InsertAfterHead], /// Enter insert mode after cursor
123        "I" => vec![InsertAtLineNonBlankStart], /// Enter insert mode at first non-blank
124        "A" => vec![InsertAtLineEnd], /// Enter insert mode at line end
125
126        "r" => vec![EnterReplaceMode], /// Replace characters
127        "y" => vec![Yank], /// Yank
128        "c" => vec![Yank, Delete, EnterInsertMode], /// Change
129        "d" => vec![Yank, Delete, Commit], /// Delete
130        "<a-c>" => vec![Delete, EnterInsertMode], /// Change without yanking
131        "<a-d>" => vec![Delete, Commit], /// Delete without yanking
132
133        "p" => vec![PasteAfter, Commit], /// Paste after
134        "P" => vec![PasteBefore, Commit], /// Paste before
135        "R" => vec![PasteReplace, Commit], /// Paste replacing selection
136        "<a-p>" => vec![PasteAllAfter, Commit], /// Paste all after
137        "<a-P>" => vec![PasteAllBefore, Commit], /// Paste all before
138        "<a-R>" => vec![PasteAllReplace, Commit], /// Paste all replacing selection
139
140        "u" => vec![Undo], /// Undo
141        "U" => vec![Redo], /// Redo
142
143        // TODO: With a count, `g` runs `GotoLine` instead of entering goto mode.
144        "gk" => vec![GotoMoveToStart], /// Move to buffer start
145        "gj" => vec![GotoMoveToBottom], /// Move to last line
146        "ge" => vec![GotoMoveToEnd], /// Move to buffer end
147        "gh" => vec![GotoMoveToLineStart], /// Move to line start
148        "gi" => vec![GotoMoveToLineNonBlankStart], /// Move to first non-blank
149        "gl" => vec![GotoMoveUntilLineEnd], /// Move to line end
150        "gK" => vec![GotoExtendToStart], /// Extend to buffer start
151        "gJ" => vec![GotoExtendToBottom], /// Extend to last line
152        "gE" => vec![GotoExtendToEnd], /// Extend to buffer end
153        "gH" => vec![GotoExtendToLineStart], /// Extend to line start
154        "gI" => vec![GotoExtendToLineNonBlankStart], /// Extend to first non-blank
155        "gL" => vec![GotoExtendUntilLineEnd], /// Extend to line end
156
157        "f" => vec![EnterSeekMode { select: Move, include: Onto, direction: Next }], /// Move onto next character
158        "<a-f>" => vec![EnterSeekMode { select: Move, include: Onto, direction: Prev }], /// Move onto previous character
159        "t" => vec![EnterSeekMode { select: Move, include: Until, direction: Next }], /// Move until next character
160        "<a-t>" => vec![EnterSeekMode { select: Move, include: Until, direction: Prev }], /// Move until previous character
161        "F" => vec![EnterSeekMode { select: Extend, include: Onto, direction: Next }], /// Extend onto next character
162        "<a-F>" => vec![EnterSeekMode { select: Extend, include: Onto, direction: Prev }], /// Extend onto previous character
163        "T" => vec![EnterSeekMode { select: Extend, include: Until, direction: Next }], /// Extend until next character
164        "<a-T>" => vec![EnterSeekMode { select: Extend, include: Until, direction: Prev }], /// Extend until previous character
165
166        "%" => vec![SelectAll], /// Select all
167        "s" => vec![SelectRegex], /// Select regex matches
168        "<a-s>" => vec![SplitIntoLines], /// Split into lines
169        "x" => vec![ExpandToFullLines], /// Expand to full lines
170        "<a-;>" => vec![Flip], /// Flip selection
171        "<a-:>" => vec![FlipForward], /// Flip selection forward
172        "(" => vec![RotateSelectionBackward], /// Rotate selection backward
173        ")" => vec![RotateSelectionForward], /// Rotate selection forward
174        "," => vec![KeepPrimary], /// Keep only primary selection range
175        ";" => vec![Collapse], /// Collapse selection ranges
176
177        "o" => vec![InsertLineBelow], /// Enter insert mode on new line below
178        "O" => vec![InsertLineAbove], /// Enter insert mode on new line above
179        "<a-o>" => vec![AddLineBelow, Commit], /// Add line below
180        "<a-O>" => vec![AddLineAbove, Commit], /// Add line above
181
182        ":" => vec![EnterCommandMode], /// Enter command mode
183
184        "<c-u>" => vec![ScrollHalfPageUp], /// Scroll half page up
185        "<c-d>" => vec![ScrollHalfPageDown], /// Scroll half page down
186        "<c-b>" => vec![ScrollFullPageUp], /// Scroll page up
187        "<c-f>" => vec![ScrollFullPageDown], /// Scroll page down
188
189        _keys => {
190            KeymapResult::Fallback(vec![ClearCount])
191        }, /// Clear count
192    }
193});
194
195pub fn handle_actions(editor: &mut Editor, actions: &[Action]) {
196    for action in actions {
197        handle_action(editor, action);
198    }
199}
200
201pub fn handle_action(editor: &mut Editor, action: &Action) {
202    match action {
203        Action::AppendCountDigit(digit) => {
204            let digit = usize::from(*digit);
205            let old_count = editor.count.map_or(0, |count| usize::from(count));
206            let new_count = NonZeroUsize::new(old_count.saturating_mul(10).saturating_add(digit));
207            editor.count = new_count;
208        }
209        Action::ClearCount => editor.count = None,
210        Action::Commit => editor.focused_buffer_mut().text.commit(),
211        Action::EnterNormalMode => enter(editor),
212        Action::EnterCommandMode => enter_command_mode(editor),
213        Action::EnterInsertMode => insert::enter(editor),
214        Action::EnterSeekMode {
215            select,
216            include,
217            direction,
218        } => {
219            seek::enter(editor, *select, *include, *direction);
220        }
221        Action::EnterReplaceMode => replace::enter(editor),
222        Action::InsertAfterHead => insert_after_head(editor),
223        Action::InsertAtLineNonBlankStart => insert_at_line_non_blank_start(editor),
224        Action::InsertAtLineEnd => insert_at_line_end(editor),
225        Action::InsertLineAbove => insert_line_above(editor),
226        Action::InsertLineBelow => insert_line_below(editor),
227        Action::AddLineAbove => add_line_above(editor),
228        Action::AddLineBelow => add_line_below(editor),
229        Action::MoveLeft => move_left(editor),
230        Action::MoveRight => move_right(editor),
231        Action::MoveUp => move_up(editor),
232        Action::MoveDown => move_down(editor),
233        Action::ExtendLeft => extend_left(editor),
234        Action::ExtendRight => extend_right(editor),
235        Action::ExtendUp => extend_up(editor),
236        Action::ExtendDown => extend_down(editor),
237        Action::GotoMoveToLine => goto_move_to_line(editor),
238        Action::GotoMoveToStart => goto_move_to_start(editor),
239        Action::GotoExtendToStart => goto_extend_to_start(editor),
240        Action::GotoMoveToBottom => goto_move_to_bottom(editor),
241        Action::GotoExtendToBottom => goto_extend_to_bottom(editor),
242        Action::GotoMoveToEnd => goto_move_to_end(editor),
243        Action::GotoExtendToEnd => goto_extend_to_end(editor),
244        Action::GotoMoveToLineStart => goto_move_to_line_start(editor),
245        Action::GotoExtendToLineStart => goto_extend_to_line_start(editor),
246        Action::GotoMoveToLineNonBlankStart => goto_move_to_line_non_blank_start(editor),
247        Action::GotoExtendToLineNonBlankStart => goto_extend_to_line_non_blank_start(editor),
248        Action::GotoMoveUntilLineEnd => goto_move_until_line_end(editor),
249        Action::GotoExtendUntilLineEnd => goto_extend_until_line_end(editor),
250        Action::KeepPrimary => keep_primary(editor),
251        Action::RotateSelectionBackward => rotate_selection_backward(editor),
252        Action::RotateSelectionForward => rotate_selection_forward(editor),
253        Action::Collapse => collapse(editor),
254        Action::Flip => flip(editor),
255        Action::FlipForward => flip_forward(editor),
256        Action::SelectAll => select_all(editor),
257        Action::ExpandToFullLines => expand_to_full_lines(editor),
258        Action::SplitIntoLines => split_into_lines(editor),
259        Action::SelectRegex => select_regex(editor),
260        Action::Delete => delete(editor),
261        Action::Yank => yank(editor),
262        Action::PasteAfter => paste(editor, PasteMode::After),
263        Action::PasteBefore => paste(editor, PasteMode::Before),
264        Action::PasteReplace => paste(editor, PasteMode::Replace),
265        Action::PasteAllAfter => paste_all(editor, PasteMode::After),
266        Action::PasteAllBefore => paste_all(editor, PasteMode::Before),
267        Action::PasteAllReplace => paste_all(editor, PasteMode::Replace),
268        Action::Undo => undo(editor),
269        Action::Redo => redo(editor),
270        Action::ScrollHalfPageUp => scroll_half_page_up(editor),
271        Action::ScrollHalfPageDown => scroll_half_page_down(editor),
272        Action::ScrollFullPageUp => scroll_full_page_up(editor),
273        Action::ScrollFullPageDown => scroll_full_page_down(editor),
274    }
275}
276
277pub fn handle_keys(editor: &mut Editor) -> bool {
278    match KEYMAP.get_keys(&editor.pending_keys) {
279        KeymapResult::Mapped(actions) => {
280            editor.pending_keys.clear();
281            handle_actions(editor, actions);
282            true
283        }
284        KeymapResult::Fallback(actions) => {
285            editor.pending_keys.clear();
286            handle_actions(editor, &actions);
287            true
288        }
289        KeymapResult::Unmapped => {
290            editor.pending_keys.clear();
291            false
292        }
293        KeymapResult::Pending => true,
294    }
295}
296
297pub fn enter(editor: &mut Editor) {
298    editor.focused_buffer_mut().text.commit();
299    editor.mode = Mode::Normal;
300    editor.count = None;
301}
302
303fn extend_left(editor: &mut Editor) {
304    let count = editor.count.take().unwrap_or(NonZeroUsize::MIN).get();
305    let mut window = editor.focused_window_mut();
306    window
307        .selection_move()
308        .for_each_move(|mut range| range.extend_left(count));
309    window.selection_move().merge_overlapping();
310    window.scroll_to_selection();
311}
312
313fn move_left(editor: &mut Editor) {
314    let count = editor.count.take().unwrap_or(NonZeroUsize::MIN).get();
315    let mut window = editor.focused_window_mut();
316    window
317        .selection_move()
318        .for_each_move(|mut range| range.move_left(count));
319    window.selection_move().merge_overlapping();
320    window.scroll_to_selection();
321}
322
323fn extend_right(editor: &mut Editor) {
324    let count = editor.count.take().unwrap_or(NonZeroUsize::MIN).get();
325    let mut window = editor.focused_window_mut();
326    window
327        .selection_move()
328        .for_each_move(|mut range| range.extend_right(count));
329    window.selection_move().merge_overlapping();
330    window.scroll_to_selection();
331}
332
333fn move_right(editor: &mut Editor) {
334    let count = editor.count.take().unwrap_or(NonZeroUsize::MIN).get();
335    let mut window = editor.focused_window_mut();
336    window
337        .selection_move()
338        .for_each_move(|mut range| range.move_right(count));
339    window.selection_move().merge_overlapping();
340    window.scroll_to_selection();
341}
342
343fn extend_up(editor: &mut Editor) {
344    let count = editor.count.take().unwrap_or(NonZeroUsize::MIN).get();
345    let mut window = editor.focused_window_mut();
346    window
347        .selection_move()
348        .for_each_move(|mut range| range.extend_up(count));
349    window.selection_move().merge_overlapping();
350    window.scroll_to_selection();
351}
352
353fn move_up(editor: &mut Editor) {
354    let count = editor.count.take().unwrap_or(NonZeroUsize::MIN).get();
355    let mut window = editor.focused_window_mut();
356    window
357        .selection_move()
358        .for_each_move(|mut range| range.move_up(count));
359    window.selection_move().merge_overlapping();
360    window.scroll_to_selection();
361}
362
363fn extend_down(editor: &mut Editor) {
364    let count = editor.count.take().unwrap_or(NonZeroUsize::MIN).get();
365    let mut window = editor.focused_window_mut();
366    window
367        .selection_move()
368        .for_each_move(|mut range| range.extend_down(count));
369    window.selection_move().merge_overlapping();
370    window.scroll_to_selection();
371}
372
373fn move_down(editor: &mut Editor) {
374    let count = editor.count.take().unwrap_or(NonZeroUsize::MIN).get();
375    let mut window = editor.focused_window_mut();
376    window
377        .selection_move()
378        .for_each_move(|mut range| range.move_down(count));
379    window.selection_move().merge_overlapping();
380    window.scroll_to_selection();
381}
382
383fn flip(editor: &mut Editor) {
384    let mut window = editor.focused_window_mut();
385    window
386        .selection_move()
387        .for_each_move(|mut range| range.flip());
388    window.scroll_to_selection();
389    editor.count = None;
390}
391
392fn flip_forward(editor: &mut Editor) {
393    let mut window = editor.focused_window_mut();
394    window
395        .selection_move()
396        .for_each_move(|mut range| range.flip_forward());
397    window.scroll_to_selection();
398    editor.count = None;
399}
400
401fn keep_primary(editor: &mut Editor) {
402    let mut window = editor.focused_window_mut();
403    window.selection_move().keep_primary();
404    window.scroll_to_selection();
405    editor.count = None;
406}
407
408fn rotate_selection_backward(editor: &mut Editor) {
409    let count = editor.count.take().unwrap_or(NonZeroUsize::MIN).get();
410    let mut window = editor.focused_window_mut();
411    window.selection_move().rotate_backward(count);
412    window.scroll_to_selection();
413}
414
415fn rotate_selection_forward(editor: &mut Editor) {
416    let count = editor.count.take().unwrap_or(NonZeroUsize::MIN).get();
417    let mut window = editor.focused_window_mut();
418    window.selection_move().rotate_forward(count);
419    window.scroll_to_selection();
420}
421
422fn collapse(editor: &mut Editor) {
423    let mut window = editor.focused_window_mut();
424    window
425        .selection_move()
426        .for_each_move(|mut range| range.collapse());
427    window.scroll_to_selection();
428    editor.count = None;
429}
430
431fn delete(editor: &mut Editor) {
432    if editor.focused_buffer().text.readonly {
433        editor.message = Some(Err(String::from("Buffer is readonly")));
434        editor.count = None;
435        return;
436    }
437    let mut window = editor.focused_window_mut();
438    window.selection_edit().delete();
439    window.scroll_to_selection();
440    editor.count = None;
441}
442
443fn yank(editor: &mut Editor) {
444    let values = editor
445        .focused_window()
446        .selection()
447        .contents()
448        .map(String::from)
449        .collect();
450    editor.registers.insert('"', values);
451    editor.count = None;
452}
453
454fn paste(editor: &mut Editor, paste_mode: PasteMode) {
455    let count = editor.count.take().unwrap_or(NonZeroUsize::MIN).get();
456    if editor.focused_buffer().text.readonly {
457        editor.message = Some(Err(String::from("Buffer is readonly")));
458        return;
459    }
460    let count = if paste_mode == PasteMode::Replace {
461        1
462    } else {
463        count
464    };
465    let values = editor.registers.get(&'"').cloned().unwrap_or_default();
466    let mut window = editor.focused_window_mut();
467    for _ in 1..=count {
468        window.selection_edit().paste(&values, paste_mode);
469    }
470    window.scroll_to_selection();
471}
472
473fn paste_all(editor: &mut Editor, paste_mode: PasteMode) {
474    editor.count = None;
475    if editor.focused_buffer().text.readonly {
476        editor.message = Some(Err(String::from("Buffer is readonly")));
477        return;
478    }
479    let values = editor.registers.get(&'"').cloned().unwrap_or_default();
480    if values.is_empty() {
481        editor.message = Some(Err(String::from("Nothing to paste")));
482        return;
483    }
484    let mut window = editor.focused_window_mut();
485    window.selection_edit().paste_all(&values, paste_mode);
486    window.scroll_to_selection();
487}
488
489fn undo(editor: &mut Editor) {
490    let count = editor.count.take().unwrap_or(NonZeroUsize::MIN).get();
491    if editor.focused_buffer().text.readonly {
492        editor.message = Some(Err(String::from("Buffer is readonly")));
493        return;
494    }
495    let mut window = editor.focused_window_mut();
496    for _ in 1..=count {
497        if !window.undo().unwrap() {
498            break;
499        }
500    }
501    window.scroll_to_selection();
502}
503
504fn redo(editor: &mut Editor) {
505    let count = editor.count.take().unwrap_or(NonZeroUsize::MIN).get();
506    if editor.focused_buffer().text.readonly {
507        editor.message = Some(Err(String::from("Buffer is readonly")));
508        return;
509    }
510    let mut window = editor.focused_window_mut();
511    for _ in 1..=count {
512        if !window.redo().unwrap() {
513            break;
514        }
515    }
516    window.scroll_to_selection();
517}
518
519fn select_all(editor: &mut Editor) {
520    let mut window = editor.focused_window_mut();
521    window.selection_move().select_all();
522    window.scroll_to_selection();
523    editor.count = None;
524}
525
526fn expand_to_full_lines(editor: &mut Editor) {
527    let mut window = editor.focused_window_mut();
528    window
529        .selection_move()
530        .for_each_move(|mut range| range.expand_to_full_lines());
531    window.selection_move().merge_overlapping();
532    window.scroll_to_selection();
533    editor.count = None;
534}
535
536fn split_into_lines(editor: &mut Editor) {
537    let mut window = editor.focused_window_mut();
538    window.selection_move().split_into_lines();
539    window.scroll_to_selection();
540    editor.count = None;
541}
542
543fn select_regex(editor: &mut Editor) {
544    prompt::enter(editor, "select", |editor, regex_str| {
545        if regex_str.is_empty() {
546            editor.message = Some(Err(String::from("Nothing selected")));
547            editor.count = None;
548            return;
549        }
550        let regex = Regex::builder()
551            .syntax(
552                regex_cursor::regex_automata::util::syntax::Config::new()
553                    .multi_line(true)
554                    .dot_matches_new_line(true),
555            )
556            .build(regex_str);
557        if let Ok(regex) = regex {
558            let matched = editor
559                .focused_window_mut()
560                .selection_move()
561                .select_regex(&regex);
562            if !matched {
563                editor.message = Some(Err(String::from("Nothing selected")));
564            }
565        } else {
566            editor.message = Some(Err(String::from("Invalid regex")));
567        }
568        editor.count = None;
569    });
570}
571
572fn insert_after_head(editor: &mut Editor) {
573    if editor.focused_buffer().text.readonly {
574        editor.message = Some(Err(String::from("Buffer is readonly")));
575        editor.count = None;
576        return;
577    }
578    let mut window = editor.focused_window_mut();
579    window.selection_edit().for_each_edit(|mut range| {
580        range.prepare_append();
581    });
582    window.scroll_to_selection();
583    editor.mode = Mode::Insert;
584    editor.count = None;
585}
586
587fn insert_at_line_non_blank_start(editor: &mut Editor) {
588    if editor.focused_buffer().text.readonly {
589        editor.message = Some(Err(String::from("Buffer is readonly")));
590        editor.count = None;
591        return;
592    }
593    let mut window = editor.focused_window_mut();
594    window.selection_move().for_each_move(|mut range| {
595        range.move_to_line_non_blank_start();
596    });
597    window.scroll_to_selection();
598    editor.mode = Mode::Insert;
599    editor.count = None;
600}
601
602fn insert_at_line_end(editor: &mut Editor) {
603    if editor.focused_buffer().text.readonly {
604        editor.message = Some(Err(String::from("Buffer is readonly")));
605        editor.count = None;
606        return;
607    }
608    let mut window = editor.focused_window_mut();
609    window.selection_move().for_each_move(|mut range| {
610        range.move_onto_line_end();
611    });
612    window.scroll_to_selection();
613    editor.mode = Mode::Insert;
614    editor.count = None;
615}
616
617fn insert_line_above(editor: &mut Editor) {
618    // TODO: Insert multiple cursors (like Kakoune) when count > 1
619    let count = editor.count.take().unwrap_or(NonZeroUsize::MIN).get();
620    if editor.focused_buffer().text.readonly {
621        editor.message = Some(Err(String::from("Buffer is readonly")));
622        return;
623    }
624    let mut window = editor.focused_window_mut();
625    // TODO: Switch to selection-level insert
626    window.selection_edit().for_each_edit(|mut range| {
627        range.move_to_line_start();
628        for _ in 0..count {
629            range.insert_char('\n');
630        }
631        range.move_left(1);
632    });
633    window.scroll_to_selection();
634    editor.mode = Mode::Insert;
635}
636
637fn insert_line_below(editor: &mut Editor) {
638    // TODO: Insert multiple cursors (like Kakoune) when count > 1
639    let count = editor.count.take().unwrap_or(NonZeroUsize::MIN).get();
640    if editor.focused_buffer().text.readonly {
641        editor.message = Some(Err(String::from("Buffer is readonly")));
642        return;
643    }
644    let mut window = editor.focused_window_mut();
645    // TODO: Switch to selection-level insert
646    window.selection_edit().for_each_edit(|mut range| {
647        range.move_onto_line_end();
648        for _ in 0..count {
649            range.insert_char('\n');
650        }
651    });
652    window.scroll_to_selection();
653    editor.mode = Mode::Insert;
654}
655
656fn add_line_above(editor: &mut Editor) {
657    let count = editor.count.take().unwrap_or(NonZeroUsize::MIN).get();
658    if editor.focused_buffer().text.readonly {
659        editor.message = Some(Err(String::from("Buffer is readonly")));
660        return;
661    }
662    let mut window = editor.focused_window_mut();
663    // TODO: Switch to selection-level insert
664    window.selection_edit().for_each_edit(|mut range| {
665        let snapshot = range.save();
666        range.move_to_line_start();
667        for _ in 0..count {
668            range.insert_char('\n');
669        }
670        assert!(range.restore(&snapshot));
671    });
672    window.selection_move().merge_overlapping();
673    window.scroll_to_selection();
674}
675
676fn add_line_below(editor: &mut Editor) {
677    let count = editor.count.take().unwrap_or(NonZeroUsize::MIN).get();
678    if editor.focused_buffer().text.readonly {
679        editor.message = Some(Err(String::from("Buffer is readonly")));
680        return;
681    }
682    let mut window = editor.focused_window_mut();
683    // TODO: Switch to selection-level insert
684    window.selection_edit().for_each_edit(|mut range| {
685        let snapshot = range.save();
686        range.move_onto_line_end();
687        for _ in 0..count {
688            range.insert_char('\n');
689        }
690        assert!(range.restore(&snapshot));
691    });
692    window.selection_move().merge_overlapping();
693    window.scroll_to_selection();
694}
695
696fn goto_move_to_line(editor: &mut Editor) {
697    let count = editor.count.take().unwrap_or(NonZeroUsize::MIN).get();
698    let mut window = editor.focused_window_mut();
699    let rope = window.buffer().text.rope().clone();
700    let line_index = min(count - 1, rope.len_lines_unix() - 1);
701    let byte_index = rope.line_to_byte_idx(line_index, LINE_TYPE);
702    window
703        .selection_move()
704        .for_each_move(|mut range| range.move_to(byte_index));
705    window.selection_move().merge_overlapping();
706    window.scroll_to_selection();
707}
708
709fn goto_move_to_start(editor: &mut Editor) {
710    let mut window = editor.focused_window_mut();
711    window
712        .selection_move()
713        .for_each_move(|mut range| range.move_to_start());
714    window.selection_move().merge_overlapping();
715    window.scroll_to_selection();
716    editor.count = None;
717}
718
719fn goto_extend_to_start(editor: &mut Editor) {
720    let mut window = editor.focused_window_mut();
721    window
722        .selection_move()
723        .for_each_move(|mut range| range.extend_to_start());
724    window.selection_move().merge_overlapping();
725    window.scroll_to_selection();
726    editor.count = None;
727}
728
729fn goto_move_to_bottom(editor: &mut Editor) {
730    let mut window = editor.focused_window_mut();
731    window
732        .selection_move()
733        .for_each_move(|mut range| range.move_to_bottom());
734    window.selection_move().merge_overlapping();
735    window.scroll_to_selection();
736    editor.count = None;
737}
738
739fn goto_extend_to_bottom(editor: &mut Editor) {
740    let mut window = editor.focused_window_mut();
741    window
742        .selection_move()
743        .for_each_move(|mut range| range.extend_to_bottom());
744    window.selection_move().merge_overlapping();
745    window.scroll_to_selection();
746    editor.count = None;
747}
748
749fn goto_move_to_end(editor: &mut Editor) {
750    let mut window = editor.focused_window_mut();
751    window
752        .selection_move()
753        .for_each_move(|mut range| range.move_to_end());
754    window.selection_move().merge_overlapping();
755    window.scroll_to_selection();
756    editor.count = None;
757}
758
759fn goto_extend_to_end(editor: &mut Editor) {
760    let mut window = editor.focused_window_mut();
761    window
762        .selection_move()
763        .for_each_move(|mut range| range.extend_to_end());
764    window.selection_move().merge_overlapping();
765    window.scroll_to_selection();
766    editor.count = None;
767}
768
769fn goto_move_to_line_start(editor: &mut Editor) {
770    let mut window = editor.focused_window_mut();
771    window
772        .selection_move()
773        .for_each_move(|mut range| range.move_to_line_start());
774    window.selection_move().merge_overlapping();
775    editor.count = None;
776}
777
778fn goto_extend_to_line_start(editor: &mut Editor) {
779    let mut window = editor.focused_window_mut();
780    window
781        .selection_move()
782        .for_each_move(|mut range| range.extend_to_line_start());
783    window.selection_move().merge_overlapping();
784    editor.count = None;
785}
786
787fn goto_move_to_line_non_blank_start(editor: &mut Editor) {
788    let mut window = editor.focused_window_mut();
789    window
790        .selection_move()
791        .for_each_move(|mut range| range.move_to_line_non_blank_start());
792    window.selection_move().merge_overlapping();
793    editor.count = None;
794}
795
796fn goto_extend_to_line_non_blank_start(editor: &mut Editor) {
797    let mut window = editor.focused_window_mut();
798    window
799        .selection_move()
800        .for_each_move(|mut range| range.extend_to_line_non_blank_start());
801    window.selection_move().merge_overlapping();
802    editor.count = None;
803}
804
805fn goto_move_until_line_end(editor: &mut Editor) {
806    let mut window = editor.focused_window_mut();
807    window
808        .selection_move()
809        .for_each_move(|mut range| range.move_until_line_end());
810    window.selection_move().merge_overlapping();
811    editor.count = None;
812}
813
814fn goto_extend_until_line_end(editor: &mut Editor) {
815    let mut window = editor.focused_window_mut();
816    window
817        .selection_move()
818        .for_each_move(|mut range| range.extend_until_line_end());
819    window.selection_move().merge_overlapping();
820    editor.count = None;
821}