Modal editor.
Reusable engine.
A vim-style editor in Rust — and the engine that powers it.
Multi-buffer TUI editor with window splits and tabs, a full LSP client
(diagnostics, goto, hover, completion, code actions, rename, format), fuzzy
file/buffer/grep pickers, a lazygit-adjacent
<leader>g git surface, tree-sitter highlighting via
runtime-loaded grammars, a persistent branching undo tree, and six bundled
themes. Same vim FSM (hjkl-vim) drives sqeel, buffr, and inbx.
1 // vim FSM driving a rope buffer. 2 use hjkl_engine::{Editor, Host}; 3 use hjkl_buffer::Rope; 4 5 fn main() -> io::Result<()> { 6 let mut ed = Editor::<Rope, _>::new(TuiHost); 7 ed.open("src/main.rs")?; 8 9 while let Some(key) = ed.poll()? { 10 ed.feed(key); 11 } 12 Ok(()) 13 }
why
One vim, many surfaces.
One FSM, many surfaces
Same FSM that drives sqeel, buffr, and inbx drives the standalone hjkl editor.
No I/O in the core
Bring your own renderer, your own clipboard, your own filesystem. The engine just transitions states.
Pure Rust, host-agnostic
The engine doesn't pin a renderer, clipboard, or filesystem. Same crate drives ratatui TUIs, GPU-composited overlays, and egui windows.
Trait-driven
Buffers, clipboards, search providers — all traits. Swap implementations without touching the FSM.
Property-tested
Rope invariants hold under any edit sequence. Vim grammar regression-suite covers motions, operators, text objects, and ex commands.
A real LSP client
Per-language server lifecycle, full text-sync, diagnostics, goto, hover,
completion, code actions, rename, and format — in
hjkl-lsp, usable from any host.
Branching undo, on disk
Undo history is a tree, not a stack: editing after an undo keeps the old
branch, g-/g+ traverse by change number, and
:undolist shows the tips. Delta-encoded into an
undofile, so undo/redo and your position survive
closing the file — a crash :recover restores the tree too.
It remembers where you were
Reopening a file restores the last cursor position on that buffer (the
last-moved window wins across splits), clamped to the file. Config:
restore_cursor.
Six bundled, or bring your own
tokyonight (default), catppuccin,
gruvbox, nord, dracula,
onedark via :colorscheme — plus the built-in
dark / light. Each switches UI chrome and
syntax together, and themes drive the terminal background with a
theme.transparent override.
:set that sticks
Every global :set option has a matching key under
[options] in config.toml, named exactly as
:set names it — and a :set from the command
bar is written straight back to your config file, preserving comments,
key order and quoting. Buffer-local options stay session-scoped. A
deliberate divergence from vim, where :set is transient.
MIT, all of it
Every crate, every adapter. Fork it, vendor it, ship it. No catch.
crates
One monorepo. Fifty-plus crates, each published on its own.
Everything lives in
kryptic-sh/hjkl
under crates/<name> and ships independently to crates.io
at a shared version. Most subsystems come as a pair: a renderer-agnostic
core crate plus a -tui adapter, so a GUI host can take the
model and leave the ratatui rendering behind. Add one line and go:
cargo add hjkl-editor.
The standalone editor. cargo install hjkl — multi-buffer,
splits, tabs, pickers, LSP, tree-sitter, smart indent,
.editorconfig.
Pure controller: cursor, motions, operators, registers, marks, macros.
No I/O deps. #![deny(missing_docs)] — new public API needs
rustdoc.
The vim grammar reducer. Pending-state machine for chords
(m<x>, q<x>,
@<x>, 5dd, "a5dd), count
accumulator, dot-repeat, @: last-ex replay. Vocabulary
types split out into hjkl-vim-types.
Rope-backed text buffer with cursor, edits, folds, search, and the persistent branching undo tree. O(log n) inserts and deletes, with criterion budgets CI fails on.
Facade crate: re-exports engine + buffer + spec types wired together. The "ready-to-host" surface — the one most consumers depend on.
Per-language server lifecycle, full text-sync, diagnostics, goto, hover,
completion, code actions, rename, format. Pairs with
hjkl-completion and hjkl-hover.
Runtime grammar loader — clones, compiles, and caches
tree-sitter-* grammars on demand. Bare and
@-prefixed captures both resolve;
lua-match? predicates built in. Zero grammars baked in.
SyntaxWorker background thread, render cache, and
viewport-scoped highlight passes — so a big file doesn't stall the
keystroke loop.
Backend-agnostic theme schema: TOML parse, palette interning,
tree-sitter capture fallback chain. StyleSpec is shared
across bonsai, hjkl, gpur, and any downstream renderer;
hjkl-theme-tui converts it to ratatui types.
Fuzzy picker subsystem — file walk, grep, custom sources, and a
consumer-agnostic PreviewHighlighter trait. Scoring lives
in hjkl-fuzzy.
Renderer-agnostic window/split layout machinery. Tabs live in
hjkl-tabs, the statusline model in
hjkl-statusline, jump lists in hjkl-quickfix.
Vim-modal forms with the full vim grammar inside every text field.
Powers : / / prompts and picker query lines.
Mason-style installer for LSP servers and developer tools.
hjkl-mangler is the formatter side — rustfmt, prettier,
gofmt, ruff, stylua, shfmt, taplo, and more.
In-house clipboard for the ecosystem — sync + async, text / HTML / RTF /
image/png, with Wayland, X11, macOS, Windows, and OSC 52
(SSH) backends. Bridges "+ / "* to the OS.
Shared TOML config loader: XDG paths (via hjkl-xdg),
span-aware parse errors, opt-in Validate hook, deep-merged
defaults + user overrides. Unknown keys are an error.
Headless neovim diff harness. Runs a keystroke stream through both real vim and hjkl and fails on divergence — the vim-compatibility regression net.
install
Prebuilt binaries on every tagged release.
macOS via Homebrew tap, Arch via AUR, or grab a prebuilt binary. Linux (x86_64, aarch64) — .tar.gz / .deb / .rpm / .apk / musl. macOS (Apple Silicon, Intel) — .tar.gz. Windows (x64) — .zip. SHA-256 sidecars on every artifact.
$ brew install kryptic-sh/tap/hjkl $ hjkl
$ yay -S hjkl-bin $ paru -S hjkl-bin
# download .apk from releases page, then: $ apk add --allow-untrusted hjkl-*.apk $ hjkl
# crates.io $ cargo install hjkl # or build from the repo $ git clone https://github.com/kryptic-sh/hjkl $ cd hjkl $ cargo build --release