Skip to main content

hjkl_vim/
lib.rs

1pub mod cmd;
2pub mod count;
3pub mod descriptors;
4pub mod editor_ext;
5pub mod insert;
6pub mod motion;
7pub mod normal;
8pub mod operator;
9pub mod pending;
10pub mod search_prompt;
11mod step;
12pub mod vim;
13mod vim_state;
14
15pub use cmd::EngineCmd;
16pub use count::CountAccumulator;
17pub use editor_ext::VimEditorExt;
18pub use operator::OperatorKind;
19pub use pending::{Key, Outcome, PendingState, step};
20/// Build an `Editor` that interprets keys as vim, or retro-fit the discipline
21/// onto one that already exists.
22///
23/// `Editor::new` leaves the discipline slot empty (the engine cannot name a
24/// concrete discipline), so an editor built through it ignores vim keys. Every
25/// vim-driven editor goes through one of these two (#267).
26pub use vim::{install as install_vim_discipline, vim_editor};
27
28/// Mode discriminator for the hjkl editor stack.
29///
30/// Used as the mode parameter in `hjkl-keymap`'s generic `Keymap<A, M: Mode>`.
31/// Satisfies the `hjkl_keymap::Mode` trait via its blanket impl for any
32/// `Copy + Eq + Hash + Debug` type.
33#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
34pub enum Mode {
35    Normal,
36    Insert,
37    Visual,
38    VisualLine,
39    VisualBlock,
40    OpPending,
41    CommandLine,
42}
43
44/// Drive the vim FSM with a [`hjkl_engine::PlannedInput`]. Translates the
45/// planned input to engine [`hjkl_engine::Input`], dispatches through
46/// [`dispatch_input`], and emits cursor-shape changes.
47///
48/// Returns `true` if the engine consumed the keystroke. Returns `false` for
49/// variants the legacy FSM does not dispatch (`Mouse`, `Paste`, `FocusGained`,
50/// `FocusLost`, `Resize`) and for special-key variants that map to `Key::Null`.
51pub fn feed_input<H: hjkl_engine::Host>(
52    editor: &mut hjkl_engine::Editor<hjkl_buffer::View, H>,
53    input: hjkl_engine::PlannedInput,
54) -> bool {
55    let Some(event) = hjkl_engine::decode_planned_input(input) else {
56        return false;
57    };
58    let consumed = dispatch_input(editor, event);
59    editor.emit_cursor_shape_if_changed();
60    consumed
61}
62
63/// Drive the vim FSM with one [`hjkl_engine::Input`].
64///
65/// This is the sole entry-point that decouples callers from the engine's
66/// internal FSM. Returns `true` if the engine consumed the keystroke.
67///
68/// # Phase 6.6c / 6.6d / 6.6e
69///
70/// Search-prompt mode (6.6c) is intercepted here before `begin_step` because
71/// it is a true short-circuit (no prelude/epilogue needed).
72///
73/// Insert mode (6.6d) is hosted in `hjkl-vim::insert::step_insert`.
74///
75/// Normal / Visual / VisualLine / VisualBlock / operator-pending modes (6.6e)
76/// are hosted in `hjkl-vim::normal::step_normal`. Both are wrapped with
77/// `begin_step` / `end_step` so macro recording, viewport scrolling, and
78/// `current_mode` sync all fire correctly.
79pub fn dispatch_input<H: hjkl_engine::Host>(
80    editor: &mut hjkl_engine::Editor<hjkl_buffer::View, H>,
81    input: hjkl_engine::Input,
82) -> bool {
83    // Search-prompt intercept: short-circuits before begin_step because it
84    // needs no prelude/epilogue.
85    if editor.search_prompt_state().is_some() {
86        return search_prompt::step_search_prompt(editor, input);
87    }
88    // Run the prelude (timestamps, chord-timeout, macro-stop, snapshots).
89    let bk = match step::begin_step(editor, input) {
90        Ok(bk) => bk,
91        Err(consumed) => return consumed,
92    };
93    // Per-mode FSM dispatch — hjkl-vim hosts all modes.
94    let consumed = match editor.vim_mode() {
95        hjkl_engine::VimMode::Insert => insert::step_insert(editor, input),
96        _ => normal::step_normal(editor, input),
97    };
98    // Run the epilogue (marks, one-shot-normal, sync, recorder, mode sync).
99    step::end_step(editor, input, bk, consumed)
100}