hedit is a lightweight, terminal-based text editor written in hica.
It pairs modern editor UX (standard keybindings, mouse support, split panes, syntax
highlighting) with hica's algebraic effects and Perceus-based memory management (FBIP).
- Familiar keybindings —
Ctrl-s/Ctrl-o/Ctrl-qfor save/open/quit, readline-style motions (Ctrl-a/e,Meta-f/b,Ctrl-k,Ctrl-w, …), undo/redo, a shared clipboard - Selections — set a mark with
Ctrl-Space, extend it with the arrow keys, then copy/cut/paste just the selected text - Incremental search —
Ctrl-fto search live as you type,Ctrl-Right/Ctrl-Leftto jump between wrapping matches - Multiple buffers — a buffer ring (
Meta-o/n/p/w) with a tabline showing every open file - Split panes — vertical and horizontal splits (
Meta-v/Meta-h), focus movement between panes, mouse-driven divider resizing - Mouse support — click to place the cursor, drag to select, click/drag to move focus or resize a split, scroll to move the viewport
- Syntax highlighting — a fast, built-in line-by-line lexer highlights
hica/kokasource - Live keybindings overlay —
Ctrl-gshows every currently bound chord, generated straight from the active bindings - Scriptable — settings, keybindings, and plugins are all just HiLisp (see below)
Full reference with every chord: docs/hedit-cheatsheet.md
(or press Ctrl-g inside hedit for a live overlay).
Using standard curl:
curl -fsSL https://github.com/cladam/hedit/releases/latest/download/install.sh | shOr install hedit using hicurl:
hicurl https://github.com/cladam/hedit/releases/latest/download/install.sh | shInstalls binary (macos-arm64, linux-arm64, linux-x86_64) to ~/.local/bin. Override target location with HEDIT_INSTALL_DIR=/usr/local/bin.
HEDIT_INSTALL_DIR=/usr/local/bin curl -fsSL https://github.com/cladam/hedit/releases/latest/download/install.sh | sh
# Or with hicurl
HEDIT_INSTALL_DIR=/usr/local/bin hicurl https://github.com/cladam/hedit/releases/latest/download/install.sh | shNote: No Windows support!
hica build -o hedit # compile to ./hedit
./hedit # open an empty scratch buffer
./hedit somefile.txt # open a real fileDefault keybindings (overridable from init.hl):
- File:
Ctrl-ssave,Ctrl-oopen-file prompt,Ctrl-qquit (with 2+ panes open, closes the active pane/buffer first) - Readline-style editing:
Ctrl-a/Ctrl-eline start/end,Ctrl-ddelete-forward,Ctrl-kkill-line,Ctrl-wkill-word-back,Meta-f/Meta-bword forward/back,Meta-dkill-word-forward,Meta-lkill-whole-line - Clipboard & history:
Ctrl-c/Ctrl-vcopy/paste,Ctrl-yyank (same clipboard slot as paste),Ctrl-zundo,Ctrl-rredo - Selection:
Ctrl-Spaceset/clear the mark, use arrows or navigation chords to select,Meta-aselect all; with a selection active,Ctrl-c/Ctrl-vcopy/replace just the selected text instead of the whole line - Search:
Ctrl-fopen search prompt,Ctrl-Rightnext occurance,Ctrl-Leftprevious occurance. - Buffers:
Meta-o/Meta-n/Meta-p/Meta-wnew/next/prev/close buffer - Split panes:
Meta-v/Meta-hopen a vertical/horizontal split prompt (type a path + Enter to open that file in the new pane, or a bare Enter to duplicate the current buffer),Meta-Arrowsmove focus to the nearest pane,Meta-Tabcycles through panes - Help:
Ctrl-gtoggle the keybindings overlay
hedit # empty scratch buffer
hedit file.txt # open a file
hedit +42 file.txt # open at line 42
hedit +42:8 file.txt # open at line 42, column 8
hedit --readonly file.txt # -R — open read-only (Save disabled)
hedit --tabsize 2 file.txt # override tabsize for this run
hedit --config init.hl file.txt # load config from elsewhere
hedit --no-config file.txt # skip init.hl entirely
hedit --help / --versionCLI flags always win over init.hl, e.g. --tabsize overrides a (set "tabsize" ...) in the loaded config.
Because HiLisp is a submodule:
git clone --recurse-submodules https://github.com/cladam/hedit.git
# or, if already cloned:
git submodule update --init --recursiveTo bump the pinned HiLisp version:
git submodule update --remote lib/hilisphedit uses HiLisp (a small Lisp
interpreter written in hica) as its configuration and plugin language.
Settings, keybindings, and plugins are all written as .hl files evaluated by the embedded
HiLisp interpreter.
HiLisp lives as a git submodule under lib/hilisp/ and is compiled together
with hedit (see hica.hml).
;; ~/.config/hedit/init.hl
(set "tabsize" 4)
(set "auto-indent" true)
(set "theme" "ilseon")
(bind "Ctrl-s" 'save)
(bind "Ctrl-q" 'quit)
(bind "Meta-w" 'close-buffer)Config discovery order (first hit wins):
$XDG_CONFIG_HOME/hedit/init.hl(or$HOME/.config/hedit/init.hl)$HOME/.hedit.hl
If neither exists, hedit runs with hard-coded defaults.
See examples/init.hl for all available options
| Built-in | Purpose |
|---|---|
(set key value) |
Set a configuration value |
(get key) |
Read a configuration value |
(bind keystroke action) |
Map a keystroke to a named built-in action |
(plugin "name") |
Opt into loading plugins/<name>/plugin.hl (see Plugins below) |
(on 'event (fn (...) ...)) |
Register a hook for a lifecycle event (see Plugins below) |
A plugin is a regular .hl file, hedit doesn't auto-discovers plugins,
you opt in by name from init.hl, and hedit resolves that name to a file next to init.hl itself.
1. Write the plugin file at plugins/<name>/plugin.hl, in the same
directory as the init.hl that will load it (i.e. next to whichever
one of $XDG_CONFIG_HOME/hedit/init.hl, $HOME/.config/hedit/init.hl,
or $HOME/.hedit.hl you're using):
;; ~/.config/hedit/plugins/greeter/plugin.hl
(on 'buffer-open (fn (path) "Welcome to hedit!"))2. Wire it up with (plugin "name") in init.hl:
;; ~/.config/hedit/init.hl
(plugin "greeter")Available hooks, registered with (on 'event (fn (...) ...))
(more than one plugin/hook can register for the same event; they run
in registration order):
| Event | Args | Fires |
|---|---|---|
'buffer-open |
(path) |
A buffer finished loading ("" for a scratch buffer); on startup and on new-buffer/open |
'pre-save |
(path) |
Before writing a named buffer to disk |
'post-save |
(path) |
After a successful save |
'pre-action |
(action-name) |
Before every resolved action (the same name (bind ...) targets) |
'quit |
() |
Once, best-effort, right before hedit exits |
Two conventions apply across all hooks instead of new builtins:
- Cancel: if any
pre-save/pre-actionhook returnsfalse, the save/action is skipped.Quitis the one exception — it always proceeds no matter what apre-actionhook returns, so a plugin can never trap the editor open. - Status: if a hook returns a string, it becomes the next status-bar message.
A broken or missing plugin.hl surfaces a one-line status message
naming the plugin and never blocks init.hl or any other plugin from
loading.
See examples/plugins/ for six reference plugins and examples/init.hl
for the (plugin ...) opt-in syntax.
MIT – see LICENSE.


