isaias-dias-machado

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!

Feedback welcome!

Most Liked

rhcarvalho

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 :slight_smile:

nikfp

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.

isaias-dias-machado

isaias-dias-machado

I feel you! Let me know if everything works smoothly or not.

nikfp

nikfp

Does the cache speed improve on subsequent runs because it’s only compiling user land code? Whereas first run also caches and prepares dependencies?

rhcarvalho

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.

Where Next?

Popular in Announcing Top

mspanc
I am pleased to announce an initial release of the Membrane Framework - an Elixir-based framework with special focus on processing multim...
New
RobinBoers
Renders Jinja templates, either from disk or defined at runtime, with support for all native Jinja features (including extends). It does...
New
sonic182
Hi everyone, at Doofinder we have been building llm_composer for some new apps, and we thought it could be useful to share it with the co...
New
bradley
I’ve been working with Claude Code extensively and absolutely love it. However, I’ve come across the challenge of managing configuration ...
New
bentanweihao
Here’s a preview: Here’s the link to download: GenServer cheatsheet Tell me what you think!
New
type1fool
WebAuthnLiveComponent WebAuthnComponents See this post about renaming the package. Passwordless authentication for Phoenix LiveView app...
New
fhunleth
Elixir Circuits is a set of libraries for interacting with hardware. We previously announced Circuits.UART, and now we’re ready to announ...
New
aesmail
Hello guys, I have finally made it. I created an admin interface for a framework. It’s been on my todo list for years and with the curre...
New
belaustegui
Announcing ErrorTracker, an Elixir-based built-in error tracking solution. Features Basic, free, built-in error tracking solution. Trac...
New
jallum
Hey folks :waving_hand: I’ve been building a distributed key-value store in Elixir/OTP called Bedrock. It implements FoundationDB’s arch...
New

Other popular topics Top

dotdotdotPaul
Okay, I'm having a heck of a time trying to figure out how to best handle the validation of belongs_to associations in Ecto. I'm sure I'...
New
lessless
I believe there are people here who are dealing with CSV files import on the daily basis, and since Excel is a really popular tool there ...
New
gshaw
What is the idiomatic way of matching for not nil in Elixir? E.g., First way: defp halt_if_not_signed_in(conn, signed_in_account) when...
New
itssasanka
Hi all, Trying to get some more clarity over utc_datetime and naive_datetime for Ecto: https://hexdocs.pm/ecto/Ecto.Schema.html#module-...
New
grych
Hi folks, Few months ago I have announced the proof-of-concept of the library to manipulate the browsers DOM objects directly from Elixi...
639 49522 488
New
mcarvalho
What is the difference between System.get_env and Application.get_env? For example, what are best practices to use one versus another.
New
fireproofsocks
Forgive me if this is obvious, but how does one delete a database record WITHOUT selecting it first? https://hexdocs.pm/ecto/Ecto.Repo.h...
New
Qqwy
Original source of discussion: This topic on the Pragmatic Programmers' Functional Web Development with Elixir, OTP, and Phoenix forum. ...
New
Nvim
Elixir appears to be a superior language to Python. I don’t see any advantage of Python over Elixir. Are there any?
New
vrod
I am using the Starship cross-shell prompt – it seems pretty nice, but I get some errors: [WARN] - (starship::utils): Executing command ...
New

We're in Beta

About us Mission Statement