hedit is a lightweight, terminal-based text editor written in hica.
It aims to pair modern editor UX (standard keybindings, mouse, multiple cursors, split views) with hica's
algebraic effects and Perceus-based memory management (FBIP).
hedit uses HiLisp (a small Lisp
interpreter written in hica) as its configuration and plugin language.
There is no second scripting runtime planned: settings, keybindings, and
(later) plugins are all authored as .hl files evaluated by the same 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 just another .hl file — there's no separate plugin
format or manager. hedit never auto-discovers plugins by scanning a
folder; 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 — this is the
step that's easy to forget, since creating the plugins/ folder alone
does nothing:
;; ~/.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 two reference plugins
(greeter, word-count) and examples/init.hl
for the (plugin ...) opt-in syntax.
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!
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/hilisphica build -o hedit # compile to ./hedit
./hedit # open an empty scratch buffer
./hedit somefile.txt # open a real fileDefault keybindings (overridable from init.hl).
Full reference with every chord: docs/hedit-cheatsheets.md
(or press Ctrl-g/Meta-h inside hedit for a live overlay).
- File:
Ctrl-ssave,Ctrl-oopen-file prompt,Ctrl-qquit - Readline-style editing:
Ctrl-a/Ctrl-eline start/end,Ctrl-b/Ctrl-fleft/right,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 - Buffers:
Meta-o/Meta-n/Meta-p/Meta-wnew/next/prev/close buffer - Help:
Ctrl-g/Meta-htoggle 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.
MIT – see LICENSE.