1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
//! Vim-mode engine.
//!
//! Implements a command grammar of the form
//!
//! ```text
//! Command := count? (operator count? (motion | text-object)
//! | motion
//! | insert-entry
//! | misc)
//! ```
//!
//! The parser is a small state machine driven by one `Input` at a time.
//! Motions and text objects produce a [`Range`] (with inclusive/exclusive
//! / linewise classification). A single [`Operator`] implementation
//! applies a range — so `dw`, `d$`, `daw`, and visual `d` all go through
//! the same code path.
//!
//! The most recent mutating command is stored in
//! [`VimState::last_change`] so `.` can replay it.
//!
//! # Roadmap
//!
//! Tracked in the original plan at
//! `~/.claude/plans/look-at-the-vim-curried-fern.md`. Phases still
//! outstanding — each one can land as an isolated PR.
//!
//! ## P3 — Registers & marks
//!
//! - TODO: `RegisterBank` indexed by char:
//! - unnamed `""`, last-yank `"0`, small-delete `"-`
//! - named `"a-"z` (uppercase `"A-"Z` appends instead of overwriting)
//! - blackhole `"_`
//! - system clipboard `"+` / `"*` (wire to `crate::clipboard::Clipboard`)
//! - read-only `":`, `".`, `"%` — surface in `:reg` output
//! - TODO: route every yank / cut / paste through the bank. Parser needs
//! a `"{reg}` prefix state that captures the target register before a
//! count / operator.
//! - TODO: `m{a-z}` sets a mark in a `HashMap<char, (buffer_id, row, col)>`;
//! `'x` jumps to the line (FirstNonBlank), `` `x `` to the exact cell.
//! Uppercase marks are global across tabs; lowercase are per-buffer.
//! - TODO: `''` and `` `` `` jump to the last-jump position; `'[` `']`
//! `'<` `'>` bound the last change / visual region.
//! - TODO: `:reg` and `:marks` ex commands.
//!
//! ## P4 — Macros
//!
//! - TODO: `q{a-z}` starts recording raw `Input`s into the register;
//! next `q` stops.
//! - TODO: `@{a-z}` replays the register by re-feeding inputs through
//! `step`. `@@` repeats the last macro. Nested macros need a sane
//! depth cap (e.g. 100) to avoid runaway loops.
//! - TODO: ensure recording doesn't capture the initial `q{a-z}` itself.
//!
//! ## P6 — Polish (still outstanding)
//!
//! - TODO: indent operators `>` / `<` (with line + text-object targets).
//! - TODO: format operator `=` — map to whatever SQL formatter we wire
//! up; for now stub that returns the range unchanged with a toast.
//! - TODO: case operators `gU` / `gu` / `g~` on a range (already have
//! single-char `~`).
//! - TODO: screen motions `H` / `M` / `L` once we track the render
//! viewport height inside Editor.
//! - TODO: scroll-to-cursor motions `zz` / `zt` / `zb`.
//!
//! ## Known substrate / divergence notes
//!
//! - TODO: insert-mode indent helpers — `Ctrl-t` / `Ctrl-d` (increase /
//! decrease indent on current line) and `Ctrl-r <reg>` (paste from a
//! register). `Ctrl-r` needs the `RegisterBank` from P3 to be useful.
//! - TODO: `/` and `?` search prompts still live in `the host/src/lib.rs`.
//! The plan calls for moving them into the editor (so the editor owns
//! `last_search_pattern` rather than the TUI loop). Safe to defer.
pub
pub
pub
pub
pub
pub
pub
pub
pub
pub
pub
pub
pub
pub
pub
pub
pub
pub
pub
pub use ;
pub use SearchPrompt;
pub use ;
pub use ;
// Flat intra-crate namespace: the FSM was one file until #267, and its
// helpers still call each other freely across these module lines.
pub use *;
pub use *;
pub use *;
pub use *;
pub use *;
pub use *;
pub use *;
pub use *;
pub use *;
pub use *;
pub use *;
pub use *;
pub use *;
pub use *;
pub use *;
pub use *;
pub use *;
pub use *;
pub use *;
// The vim discipline's actual public API.
pub use parse_motion;
pub use ;
// `matching_tag_pair` and the tag-matching group moved to `hjkl_engine::tag`
// (#265): a pure buffer query any discipline can use, not vim grammar.
pub use ;
use Editor;
/// Install the vim discipline on `ed`, replacing whatever was there.
///
/// [`Editor::new`] leaves the discipline slot at
/// [`NoDiscipline`](hjkl_engine::NoDiscipline) — the engine cannot name a concrete
/// discipline. Every editor that should interpret keys as vim must be handed to
/// this once at construction; [`vim_editor`] does both in one call.
///
/// Dispatching vim input at an editor that skipped this panics on the first
/// state access (see `hjkl_vim::vim_state`) rather than silently behaving like
/// a keyboard-less buffer.
/// Build an [`Editor`] with the vim discipline already installed.
///
/// The vim-flavoured counterpart of [`Editor::new`], which yields an editor
/// with no discipline at all.