Skip to content

42dotmk/hed

Repository files navigation

hed

A small sized(<1MB), hackable terminal editor written in C.

Modal by default, but ships Emacs and VSCode keymaps you can swap to at runtime. Tree-sitter highlights, ripgrep / fzf integrations, window splits, tmux runner pane, OSC 52 clipboard, LSP. Every user-facing feature is a plugin you can rip out, fork, or replace.


Install in 10 seconds

One line install:

curl -fsSL https://github.com/42dotmk/hed/releases/latest/download/install.sh | bash

The installer asks two things:

  1. Source or binary? Binary is faster (one download, ready to run). Source clones the repo into ~/.local/share/hed, builds it, and symlinks hed and tsi into ~/.local/bin — pick this if you want to hack on plugins.
  2. Optional tools? Offers to download portable static fzf and ripgrep binaries into the same ~/.local/bin, then lets you pick tree-sitter grammars to install for syntax highlighting.

No sudo. No package manager. Everything ends up under ~/.local/.

After install, make sure ~/.local/bin is on your PATH:

export PATH="$HOME/.local/bin:$PATH"

Then run hed and you're in.


Build from source manually

If you want to skip the installer:

git clone --recursive https://github.com/42dotmk/hed.git
cd hed
make
./build/hed

If you forgot --recursive:

git submodule update --init --recursive

Targets:

make           # build build/hed and build/tsi
make install   # symlink build/hed and build/tsi into ~/.local/bin
make run       # build then run
make clean     # remove build/
make test      # Unity unit tests

make install symlinks rather than copies — rebuilds (make, :reload) update the installed binary automatically.

Build requirements

  • gcc or clang, C11
  • POSIX terminal, libdl

The vendored tree-sitter runtime is included as a submodule under vendor/tree-sitter and statically linked. No libtree-sitter system package required.

Optional runtime tools

Each integration degrades cleanly if its tool is missing:

Tool Used by
ripgrep :rg, :ssearch, :rgword
fzf :fzf, :recent, :c, history pickers
tmux runner pane (:tmux_toggle, :tmux_send_line)
lazygit :git
bat fzf previews
ctags :tag
clang-format / rustfmt / prettier / black / gofmt / shfmt :fmt
git, cc tsi (tree-sitter grammar installer)

Highlights

  • Vim-like modal editing by default, with full operator + text-object composition (diw, ci(, ya", >i{, …) and the usual undo / redo, registers, macros, and search stack.
  • Three swappable keymaps: Vim (default), Emacs (modeless, C-a/C-e/C-x cluster), VSCode (modeless, Ctrl+S/Z/F/D, shift-arrow selection). Toggle at runtime with :keymap.
  • Tree-sitter highlighting for any language you install via tsi. Grammars load on demand with dlopen.
  • Fuzzy pickers: :fzf files, :recent recent, :c commands, history fzf, jump-list fzf — all powered by fzf.
  • ripgrep + quickfix: :rg, :rgword, :ssearch populate a quickfix buffer with live preview as the cursor moves.
  • Splits & windows: vertical / horizontal splits, window focus navigation, modal floating windows.
  • tmux runner pane: send the paragraph under your cursor to a shell pane next door.
  • System clipboard over SSH via OSC 52 — no xclip, pbcopy, or wl-copy shelling out.
  • :reload rebuilds the editor and execs the new binary in place, restoring all open buffers.
  • No flicker on terminals that support DEC mode 2026 (kitty, alacritty, wezterm, foot, ghostty).

Plugins

The core editor knows about buffers, windows, terminal I/O, the keybind dispatcher, the command registry, and the hook system. Every user-facing feature is a plugin in plugins/<name>/. Each has its own README; here's the catalogue:

Plugin What it does
core Default : command set (:q, :w, :e, :bn, :fzf, :rg, …)
vim_keybinds Default modal Vim keymap
emacs_keybinds Modeless Emacs keymap (C-a/C-e, M-x, C-x cluster)
vscode_keybinds Modeless VSCode keymap (Ctrl+S, shift-arrow selection)
keymap :keymap and :keymap-toggle for runtime swap
treesitter Syntax highlighting; grammars via dlopen
clipboard OSC 52 yank to system clipboard (works over SSH)
dired oil.nvim-style directory browser
tmux Runner pane integration
fmt :fmt runs an external formatter on the buffer
auto_pair Auto-insert matching brackets and quotes
smart_indent Carry indent onto new lines
quickfix_preview Live preview of the quickfix entry under the cursor
viewmd Markdown live preview in the browser
scratch :scratch ephemeral unnamed buffer
sed :sed <expr> pipes the buffer through external sed
reload :reload rebuilds and execs the new binary
session Save / restore the open-buffer list per cwd
lsp LSP client (work in progress)
example Starter template — copy and rename for your own

Configuration

All user-facing customization lives in src/config.c:

void config_init(void) {
    plugin_load(&plugin_core,             1);
    plugin_load(&plugin_vim_keybinds,     1);
    plugin_load(&plugin_emacs_keybinds,   0);  // registered, swappable
    plugin_load(&plugin_vscode_keybinds,  0);
    plugin_load(&plugin_treesitter,       1);
    plugin_load(&plugin_clipboard,        1);
    /* ... */

    /* Personal overrides — last-write-wins, beats plugin defaults. */
    cmapn(" ff", "fzf");
    cmapn(" rr", "reload");
    /* ... */
}

Edit, run make, and :reload from inside the editor to pick up the changes — no need to quit and relaunch.

Adding your own plugin

cp -r plugins/example plugins/myplugin
# rename example → myplugin in the source files
# add plugin_load(&plugin_myplugin, 1) to src/config.c
make

See plugins/example/README.md for the full recipe and the plugin contract.

Out-of-tree plugins

Keep your plugin set anywhere on disk and point the build at it:

make PLUGINS_DIR=$HOME/my-hed-plugins

Project layout

src/                 # core editor (buffers, windows, terminal,
                     # commands, keybinds, hooks, undo, fold, …)
plugins/             # all user-facing functionality (see catalogue above)
vendor/tree-sitter/  # vendored runtime, statically linked
ts/                  # tsi (grammar installer) source
queries/             # tree-sitter highlight queries by language
test/                # Unity unit tests

Troubleshooting

  • Logs: ~/.cache/hed/<encoded-cwd>/log. Tail it while hed runs; clear with :logclear.
  • :plugins lists everything currently loaded.
  • :keybinds lists every binding registered for the active mode.
  • If fzf, ripgrep, tmux, or lazygit are missing, related commands fail with a status-line message and the rest of the editor keeps working.
  • If :reload fails to rebuild, the error goes to the status line — open ~/.cache/hed/<encoded-cwd>/log for the full output.

About

c based text editor

Resources

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages