Skip to main content

hjkl_vim/
insert.rs

1//! Phase 6.6d: insert-mode FSM body relocated from `hjkl-engine::vim`.
2//!
3//! Dispatched by [`crate::dispatch_input`] before the deprecated
4//! `Editor::step_input` shim. The engine keeps an in-engine duplicate
5//! body in `vim::step` (`Mode::Insert` arm) for back-compat with the
6//! deprecated shim path until Phase 6.6h.
7use hjkl_engine::{Host, Input, Key};
8
9/// Drive the insert-mode FSM for one keystroke.
10///
11/// Returns `true` (consumed) unconditionally — every key inside insert mode
12/// is swallowed regardless of whether it produced a visible effect.
13pub fn step_insert<H: Host>(
14    ed: &mut hjkl_engine::Editor<hjkl_buffer::Buffer, H>,
15    input: Input,
16) -> bool {
17    // `Ctrl-R {reg}` paste — the previous keystroke armed the wait. Any
18    // non-char key cancels (matches vim, which beeps on selectors like
19    // Esc and re-emits the literal text otherwise).
20    if ed.insert_pending_register() {
21        ed.set_insert_pending_register(false);
22        if let Key::Char(c) = input.key
23            && !input.ctrl
24        {
25            ed.insert_paste_register(c);
26        }
27        return true;
28    }
29
30    if input.key == Key::Esc {
31        ed.leave_insert_to_normal();
32        return true;
33    }
34
35    // Ctrl-prefixed insert-mode shortcuts — thin dispatcher to public Editor methods.
36    if input.ctrl {
37        match input.key {
38            Key::Char('w') => {
39                ed.insert_ctrl_w();
40                return true;
41            }
42            Key::Char('u') => {
43                ed.insert_ctrl_u();
44                return true;
45            }
46            Key::Char('h') => {
47                ed.insert_ctrl_h();
48                return true;
49            }
50            Key::Char('o') => {
51                ed.insert_ctrl_o_arm();
52                return true;
53            }
54            Key::Char('r') => {
55                ed.insert_ctrl_r_arm();
56                return true;
57            }
58            Key::Char('t') => {
59                ed.insert_ctrl_t();
60                return true;
61            }
62            Key::Char('d') => {
63                ed.insert_ctrl_d();
64                return true;
65            }
66            Key::Char(']') => {
67                // `<C-]>` — expand abbreviation WITHOUT inserting any character.
68                ed.insert_ctrl_bracket();
69                return true;
70            }
71            _ => {}
72        }
73    }
74
75    // Widen the session's visited row window *before* handling the key
76    // so navigation-only keystrokes (arrow keys) still extend the range.
77    let (row, _) = ed.cursor();
78    if let Some(session) = ed.insert_session_mut() {
79        session.row_min = session.row_min.min(row);
80        session.row_max = session.row_max.max(row);
81    }
82    let mutated = handle_insert_key(ed, input);
83    if mutated {
84        ed.mark_content_dirty();
85        let (row, _) = ed.cursor();
86        if let Some(session) = ed.insert_session_mut() {
87            session.row_min = session.row_min.min(row);
88            session.row_max = session.row_max.max(row);
89        }
90    }
91    true
92}
93
94/// Insert-mode key dispatcher — thin shim that routes each key to the
95/// corresponding public `Editor::*` method (Phase 6.6a). Returns `true`
96/// when the buffer mutated (editing keys), `false` for navigation-only keys.
97pub(crate) fn handle_insert_key<H: Host>(
98    ed: &mut hjkl_engine::Editor<hjkl_buffer::Buffer, H>,
99    input: Input,
100) -> bool {
101    use hjkl_engine::InsertDir;
102    match input.key {
103        Key::Char(c) => {
104            ed.insert_char(c);
105            true
106        }
107        Key::Enter => {
108            ed.insert_newline();
109            true
110        }
111        Key::Tab => {
112            ed.insert_tab();
113            true
114        }
115        Key::Backspace => {
116            ed.insert_backspace();
117            true
118        }
119        Key::Delete => {
120            ed.insert_delete();
121            true
122        }
123        Key::Left => {
124            ed.insert_arrow(InsertDir::Left);
125            false
126        }
127        Key::Right => {
128            ed.insert_arrow(InsertDir::Right);
129            false
130        }
131        Key::Up => {
132            ed.insert_arrow(InsertDir::Up);
133            false
134        }
135        Key::Down => {
136            ed.insert_arrow(InsertDir::Down);
137            false
138        }
139        Key::Home => {
140            ed.insert_home();
141            false
142        }
143        Key::End => {
144            ed.insert_end();
145            false
146        }
147        Key::PageUp => {
148            let h = ed.viewport_height_value();
149            ed.insert_pageup(h);
150            false
151        }
152        Key::PageDown => {
153            let h = ed.viewport_height_value();
154            ed.insert_pagedown(h);
155            false
156        }
157        // F-keys, mouse scroll, copy/cut/paste virtual keys, Null —
158        // no insert-mode behaviour.
159        _ => false,
160    }
161}