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

and follow the prompts.

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 (or the core editor itself).

  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"

and

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

make only compiles. It builds two binaries into build/:

  • build/hed — the editor
  • build/tsi — the tree-sitter grammar installer

Then symlink both into ~/.local/bin:

make install

The extras — portable fzf/ripgrep, tree-sitter grammars, and copilot-language-server (for :copilot login) — are fetched by the one-line install.sh above, not by the build.

Man pages

make man generates roff man pages from this README and every plugins/*/README.md (one per plugin) into man/man1/hed.1, hed-tmux.1, hed-vim_keybinds.1, and so on. Requires pandoc.

The build bakes that path into the binary, so the man plugin can open them from inside the editor with :man hed, :man hed-tmux, etc. — no system install needed. To put them on the system MANPATH (so man hed / apropos hed work in any shell too):

make install-man        # -> ~/.local/share/man/man1

Plugins

Every user-facing feature is a plugin in plugins/<name>/.

Plugin What it does
aishell Toggle a tmux pane running your AI assistant (Claude, Copilot, etc.) — configurable spawn command
auto_pair Auto-insert matching brackets and quotes
autosave Idle/timer autosave to per-cwd cache dir, with recovery prompt on reopen
clipboard OSC 52 yank to system clipboard (works over SSH)
core Default : command set (:q, :w, :e, :bn, :fzf, :rg, …)
copilot GitHub Copilot ghost-text suggestions via copilot-language-server, with a [copilot] alternatives pane
ctags Ctags integration for symbol navigation
dired oil.nvim-style directory browser
emacs_keybinds Modeless Emacs keymap (C-a/C-e, M-x, C-x cluster)
example Starter template — copy and rename for your own
fmt :fmt runs an external formatter on the buffer
folds Bracket + indent fold methods and per-filetype defaults
git Git integration
hed_themes Theme management
keymap :keymap and :keymap-toggle for runtime swap
lsp LSP client (work in progress)
mail Mail integration
mail_git_patch Git patch mail integration
man Manual pages viewer
markdown Markdown rendering support
mouse Click to place cursor, drag to select, wheel to scroll (:mouse on|off|toggle)
multicursor Extra cursors that mirror every keypress (:mc_add_below, :mc_add_above)
open File opening utilities
pickers Fuzzy pickers
quickfix_preview Live preview of the quickfix entry under the cursor
reload :reload rebuilds and execs the new binary
scratch :scratch ephemeral unnamed buffer
search In-file / project search (rg / ssearch) helpers
sed :sed <expr> pipes the buffer through external sed
selectlist Selection list functionality
session Save / restore the open-buffer list per cwd
shell :shell / ! prompt; capture tokens splice stdout into the buffer or yank register
smart_indent Carry indent onto new lines
tasks Task list / TODO management
tmux Runner pane integration
translate Translation support
treesitter Syntax highlighting; grammars via dlopen
viewmd Markdown live preview in the browser
vim_keybinds Default modal Vim keymap
vscode_keybinds Modeless VSCode keymap (Ctrl+S, shift-arrow selection)
whichkey While a multi-key chord is in progress, list the candidate completions in a 1–4 column table
yazi Launch the yazi file manager as a chooser; selected paths open as buffers

Adding your own plugin

  1. cp -r plugins/example plugins/myplugin
  2. Rename examplemyplugin in the source files
  3. Add plugin_load(&plugin_myplugin, 1) to src/config.h (or load it from your user config — see Configuration)
  4. Build with 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

Quickstart

hed

A new buffer comes up.

  • :e to open files
  • :q to quit
  • :w to save

Try :fzf to explore the project.


Features

  • Modal editing (Vim-style) by default, but also ships Emacs and VSCode keymaps you can swap to at runtime (:keymap <name> to switch, :keymap-toggle to cycle)

  • Tree-sitter syntax highlighting (with grammar via dlopen)

  • Fzf integration for fuzzy file and command search

  • Tmux runner pane (to test snippets, open REPLs, etc.)

  • OSC 52 clipboard (to yank to system clipboard, works over SSH)

  • LSP integration (work in progress)

  • Split windows, reloads, sessions

  • Git, mail, AI shell, file manager (Yazi)

  • Auto-save, auto-recover, backup, undo/redo


Why

Hed is for you if:

  • You like to read documentation, not just code
  • You want a single self-contained binary (links only libdl; the tree-sitter runtime is statically vendored in). Tools like fzf, ripgrep, and tmux are optional and only used when present
  • You like to hack on your editor, not just use it
  • You are a plugin developer or plugin consumer
  • You want to reinvent the editor or tune your own

The editor was built for one thing: to be a framework and a template, not a feature-rich text editor.


Files

src/
├── main.c              # Entry point + select() loop
├── editor.c            # Core editor state, modes
├── config.h            # Base config: plugin manifest + defaults
├── hooks.c             # Event system
├── terminal.c          # ANSI rendering, sync output
├── input/              # keybinds, macros, registers, command mode
├── commands/           # command registry + built-ins
├── buf/                # buffer, rows, text objects
├── ui/                 # windows, layout, status bar
├── fs/                 # file I/O + path helpers
├── utils/              # editor helpers (undo, history, fzf, fold, …)
└── lib/                # stateless leaves (strings, theme, vector, …)

plugins/                # all user-facing functionality (catalogue above)

Plugins Directory

plugins/
├── aishell/             # AI shell (like Claude, Copilot)
├── auto_pair/           # Auto-insert matching brackets and quotes
├── autosave/            # Idle/timer autosave
├── clipboard/           # OSC 52 yank to system clipboard
├── core/                # Default `:` commands
├── copilot/             # GitHub Copilot
├── ctags/               # Ctags integration
├── dired/               # oil.nvim-style directory browser
├── emacs_keybinds/      # Emacs keymap
├── example/             # Plugin template
├── fmt/                 # External formatter
├── folds/               # Bracket + indent fold methods
├── git/                 # Git integration
├── hed_themes/          # Theme management
├── keymap/              # Runtime keymap swap
├── lsp/                 # LSP client (experimental)
├── mail/                # Mail integration
├── mail_git_patch/      # Git patch mail
├── man/                 # Manual page viewer
├── markdown/            # Markdown rendering
├── mouse/               # Mouse: click / drag-select / wheel scroll
├── multicursor/         # Extra cursors
├── open/                # File opening utilities
├── pickers/             # Fuzzy pickers
├── quickfix_preview/    # Quickfix preview
├── reload/              # Reload binary
├── scratch/             # Scratch buffer
├── search/              # In-file / project search helpers
├── sed/                 # External sed
├── selectlist/          # Selection list
├── session/             # Session management
├── shell/               # Shell integration
├── smart_indent/        # Carry indent onto new lines
├── tasks/               # Task list / TODO management
├── tmux/                # Tmux runner pane
├── translate/           # Translation support
├── treesitter/          # Tree-sitter syntax highlighting
├── viewmd/              # Markdown preview
├── vim_keybinds/        # Vim keymap
├── vscode_keybinds/     # VSCode keymap
├── whichkey/            # Which-key tooltip
└── yazi/                # Yazi file manager

Configuration

Config lives in two layers, both compiled in:

  • Base configsrc/config.h, shipped in-tree. It defines the stock plugin manifest (config_load_default_plugins()) and the default theme + leader keybinds (config_load_defaults()). Edit this to change or remove stock plugins.
  • User config~/.config/hed/config.c, optional and user-owned. If present, the Makefile compiles it in and runs it after the defaults, so it's purely additive: enable extra plugins, and override any keybind via last-write-wins.

A user config just needs to define config_user_init():

/* ~/.config/hed/config.c */
#include "hed.h"

extern const Plugin plugin_myplugin;   /* from EXTRA_PLUGIN_DIRS */

void config_user_init(void) {
  plugin_load(&plugin_myplugin, 1);    // 1 = enabled, 0 = swappable

  // Personal overrides — last-write-wins, beats plugin defaults.
  cmapn(" ff", "recent", "recent files");
}

After editing either layer, run :reload to rebuild and restart.

Keybindings

All keybindings are user-overridable. Default is:

  • :keymap shows the current keymap; :keymap <name> switches to Vim / Emacs / VSCode, and :keymap-toggle cycles through them
  • :plugins to list currently loaded plugins
  • :reload to rebuild and exec the new binary

FAQ

Can I use this with a LSP?

Yes. The LSP client is in plugins/lsp/ and is not yet enabled by default.

How do I override a keybinding?

In your user config (~/.config/hed/config.c) or in src/config.h's config_load_defaults(), use cmapn(" key", "command") to override or map keys. Later registrations win, so a user-config binding beats the stock default.

How do I write my own plugin?

See the plugins/example/README.md to get started.


License

MIT

About

c based text editor

Resources

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages