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