isaias-dias-machado
ExHelp - documentation fuzzy search and paging CLI tool for Elixir/Erlang
IEx’s h macro is great but it lacks a pager, so I built a small tool that caches documentation and lets you fuzzy search through it in your OS shell.
It follows the Unix philosophy - Internally pipes to fzf and your default $PAGER. The cache lives at ~/.cache/exh and builds up over time as you workon different projects (no duplicates).
It compiles your project, loads all the available modules, and encodes the documentation using ETF.
Performance-wise, it caches a vanilla Phoenix project in about 10 seconds on first run, 3 seconds on subsequent runs. Tested on my old T420.
The formatting of the docs matches IEx’s `h` macro since I borrowed some of that code.
Example usage:
$ exh fetch
$ exh
Originally tried making it IEx-native using Ports (check the less-in-iex branch), but, despite redirecting the group leader and waiting for the Port’s exit message, couldn’t find a way to make the BEAM fully yield terminal control - resulted in race conditions between the BEAM and the TUI. If anyone has insights on this, I’m all ears!
https://github.com/isaias-dias-machado/exhelp
Feedback welcome!
Most Liked
rhcarvalho
Interesting! How do you deal with versioning?
E.g., the help for a certain module/ function may change depending on the current branch/commit, in particular for “in development” state, not between tagged releases.
The cache invalidation problem ![]()
rhcarvalho
I find myself consuming docs most of the time from hexdocs.pm or source code. When I do use h inside IEx, I’m typically inside tmux and can “scroll” back up with the keyboard.
So, speaking hypothetically, if exh clean nukes the whole cache, it would be too aggressive if I just want to refresh docs for a particular project. A refetch or fetch --force would be more targeted.
I still wonder how are you structuring the cache. Do you shard by package version? How do you treat Elixir and Erlang documentation?
This idea of centrally storing a deduplicated cache of packages reminds me of the Go Module Cache. In the Go ecosystem published modules have an immutable cryptographic checksum, and the multiple versions of a package are cached read-only in a single place. Separately, there’s also a build cache. How I’d translate that idea to Elixir is that published Hex packages could be cached by version, while “current project” needs special treatment as it can change anytime without a version change.
nikfp
This solves a pain point I’ve been experiencing a lot, and never got a chance to dig in. (Too busy scrolling up after calling help!)
Looking forward to trying it out.








