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}