hjkl
Vim-modal terminal editor: standalone TUI built on the hjkl engine.
Part of the hjkl monorepo — a vim-modal editor in Rust.
Native vim-modal editor. Single static binary, no plugins, no config files required. Built on the hjkl-engine + rope buffer.
Install
macOS (Homebrew)
Arch Linux (AUR)
Alpine Linux
Debian / Ubuntu — .deb for amd64 + arm64 on the
releases page.
Fedora / RHEL — .rpm for x86_64 + aarch64 on the same page.
Or grab a pre-built tarball for any platform from the releases page.
Status
0.12.2 — full LSP client (5 phases: diagnostics, goto/hover, completion, code
actions/rename/format), window splits (:sp/:vsp, Ctrl-w nav, resize),
tabs, tmux-navigator handoff, mouse scroll, line numbers, multi-buffer editing,
fuzzy file/buffer/grep pickers with syntax-highlighted preview, tree-sitter
highlighting, smart indent, .editorconfig, and clipboard via
hjkl-clipboard. See
SCOPE.md for the full feature roadmap.
Usage
Headless mode
Run ex commands against files without a terminal. No ratatui, no crossterm — suitable for CI scripts and automated code-mods.
# Substitute and write back
# Same with -c flag (colon is optional)
# Loop over many files
for; do
done
Ordering: all -c CMD flags run first (in flag order), then all +cmd
tokens (in argv order). The leading : is optional in both forms.
No auto-write: like vim, hjkl never writes implicitly. Include :w, :wq,
or :x in your command stream or the file is left unchanged.
Exit codes: 0 on success; 1 if any ex command returns an error or an I/O
failure occurs.
See issue #26 for the
multi-phase roadmap (Phase 2: --embed, Phase 3: nvim-API msgpack-rpc).
Embed mode (RPC)
hjkl --embed boots without a TUI and speaks JSON-RPC 2.0 over stdin/stdout
(newline-delimited, one request per line, one response per line). External code
can drive a live editor FSM — feed keystrokes, run ex commands, query buffer
state, cursor position, mode, and registers.
|
See docs/embed-rpc.md for the full method
catalogue, error codes, and examples. Phase 2 of
issue #26.
hjkl --nvim-api speaks the msgpack-rpc wire protocol with nvim-compatible
method names (nvim_buf_set_lines, nvim_input, nvim_command, etc.) so
existing nvim-rs clients can target hjkl unchanged. Phase 3 of
issue #26.
What works
- Normal / Insert / Visual / Command modes with full mode-indicator cursor shape
- All standard motions, operators, and text objects (free from the engine FSM)
- Status line: filename, mode, cursor position, dirty marker;
REC@rbadge while recording a macro; pending count + operator; search count[n/m] - Cursor-line background (subtle blue-grey; suppressed during
://prompts) ~tilde markers on rows past end-of-buffer (vimNonTextstyle):wsave,:qquit,:wq/:xwrite-quit,:eopen file:setoptions,:%ssearch-and-replace with confirmation prompt:!cmdshell exec,:r !cmd/:r fileread-into-buffer:reg,:marks,:jumps,:changes— output shown as a centered info popup//?incremental search with match highlighting- Undo / redo, marks, registers (shared across buffer slots)
- Terminal resize handled mid-frame
- Read-only guard (
-Rflag + engine-level mutation block) - Jump to line (
+N) and search-on-open (+/pattern) :set number/:set relativenumberline-number gutter. Aliasesnu/rnu/nonu/nornu; combinednu rnuenables vim hybrid mode. Plus:set numberwidth=Nfor minimum gutter width.- Multi-buffer: open many files; tab line at top when more than one buffer
is open; switch with
:bn/:bp/:bd[!]/:bfirst/:blast/:b N/:b name/:ls/:buffers; alt buffer (Ctrl-^/:b#); cycle withShift-H/Shift-Landgt/gT/]b/[b; bulk save/quit with:wa/:qa[!]/:wqa[!] - Window splits —
:sp/:vsp,Ctrl-w j/k/h/l/w/Wnavigation, resize (Ctrl-w +/-/>/</=/_/|),:resize/:vertical resize,:only/Ctrl-w o,:new/:vnew; per-window cursor + viewport state; 1-cell separator between panes - Tabs —
:tabnew,gt/gT,:tabnext/:tabprev/:tabclose/:tabfirst/:tablast/:tabonly/:tabmove/:tabs,Ctrl-w T - tmux-navigator handoff —
Ctrl-h/j/k/lin Normal mode move between hjkl windows; at an edge and$TMUXis set, falls through totmux select-pane - Mouse wheel scroll — wheel scrolls viewport with cursor clamped inside,
respecting
scrolloff. Toggle witheditor.mouseconfig field or:set mouse/:set nomouse/:set mouse! - LSP — per-language server lifecycle (bundled configs for rust-analyzer,
pyright, typescript-language-server, clangd, gopls, lua-language-server):
- Diagnostics: inline + signcolumn rendering, severity highlighting,
]d/[dmotions,:LspInfo - Goto:
gd/gD/gi/gy, hover withK, references withgr/:lreferences - Completion: triggered + manual popup, kind icons, snippet expansion, async resolve
- Code actions:
<leader>ca/:LspCodeAction - Rename:
<leader>rn/:LspRename - Format:
:LspFormat/:LspFormatRangewith workspace-edit application - Status-line spinner while LSP requests are in flight
- Diagnostics: inline + signcolumn rendering, severity highlighting,
- Fuzzy file picker (
<Space><Space>/<Space>f/:picker/hjkl +picker) with syntax-highlighted preview - Buffer picker (
<Space>b/:bpicker) - Grep picker (
<Space>//:rg <pattern>) — ripgrep-backed content search with grep / findstr fallback; preview jumps to and highlights the match line - Tree-sitter syntax highlighting (Rust, Markdown, JSON, TOML, SQL bundled); grammar-load spinner in status line
- Comment marker overlay —
TODO/FIXME/FIX/NOTE/INFO/WARNmarkers highlighted; consecutive single-line comments inherit the marker - Smart indent — Enter /
o/Oauto-indent after{/(/[; close brace on a new line auto-dedents .editorconfigsupport —indent_style,indent_size,tab_width, andmax_line_lengthapplied on file open- Tab settings:
tabstop,softtabstop,expandtab(defaults: 4-space soft tabs); tabs render as visually aligned spaces; Backspace deletes a soft tab as a unit;:set tabstop=Nupdates rendering end-to-end - Per-buffer git diff signs (
+/~/_in the gutter) and tree-sitter diagnostic signs
What's deferred
- Plugins
Related crates
hjkl-buffer— rope-based bufferhjkl-engine— modal-editing FSMhjkl-editor— ex commands, search, shell exechjkl-bonsai— bundled tree-sitter grammars + Neovim-flavoured highlight themeshjkl-clipboard— system clipboard adapterhjkl-form— single-line form input built on the engine (used by pickers,://prompts)hjkl-picker— fuzzy picker subsystem (Picker,PickerLogic,FileSource,RgSource, scorer)hjkl-ratatui— ratatui rendering adapters + shared spinnerhjkl-lsp— LSP client crate
See docs.rs/hjkl-engine for the engine trait reference.
Links
- Website: https://hjkl.kryptic.sh
- Repository: https://github.com/kryptic-sh/hjkl
Documentation
Contributing
See the monorepo CONTRIBUTING guide.
License
MIT — see LICENSE.