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