---
## Why
In an age of autocomplete, Stack Overflow snippets, and AI that will happily invent flags, it is still worth going back to the fundamentals: the **manual** that shipped with the tool.
RTFM puts that manual under your thumb. Hit **Tab**, fuzzy-filter what you need, read the description, insert the token, without leaving the line, and without guessing.
It is a **zsh** plugin powered by **fzf**. It reads **man** pages (and `--help` when man is missing), understands common subcommands, and browses files when the command wants a path.
**Enter never runs the command.** It only inserts text onto your line.
---
## Showcase
### Options from the man page
```text
$ ls
```
A floating picker opens: left column is every option/argument from `ls(1)`, right column is the description. Type `color` or `human` to filter. **Enter** inserts the pick and returns you to the shell; **Tab** inserts and **keeps the picker open** so you can stack flags.
```text
$ ls --author --color[=WHEN] -l _
↑ already chosen tokens disappear from the next list
```
### Subcommands
```text
$ docker p → filter stays on "p" (incomplete)
$ docker ps → opens docker-ps options (--all, -q, …)
$ git sta → incomplete prefix stays as the query
$ git status → git-status man options
```
### Paths when they matter
```text
$ cat → options + files/dirs in the cwd (and /)
$ cat / → directories under / only (no man noise)
$ cd src/ → directories under src/ only
$ mv → pick src, Tab again for dst (stays open)
```
Zoom into a directory with **Tab**: the list shows **child names only** (`file`, `nested`), not `src/file`. Insert still uses the full path.
### Regex hunt inside options
```text
$ ls
Ctrl-f
regex> author|help
Enter → filtered list of matches
n / N → jump between matches
```
### Wrappers and special tools
```text
$ sudo docker ps → skips sudo; shows docker-ps options
$ ip addr → ip-address man options / verbs
$ sv status → services under $SVDIR
```
### In-picker help
Press **?** inside any picker for a short key guide (**q** to close).
---
## What you get
| Area | Behavior |
|------|----------|
| **Commands** | Tab on the first word → PATH executables + selected builtins; one match chains into options in the same Tab |
| **Options / args** | From **man** when possible, else `--help` / `-h`; sub-man pages like `git-status` |
| **Paths** | Mixed in when usage looks like it takes a file; depth-1 browse; zoom with Tab |
| **Already used** | Options and path tokens already on the line are dropped from the next picker |
| **Preview** | Man/help text for options; `ls -ld` + contents for files; directory listing for dirs. Contents of `.env`, keys, and similar names are not shown |
| **Safety** | Enter only inserts; Esc aborts; never executes the line |
---
## Install
### Requirements
- **zsh** (interactive, with ZLE)
- **fzf**
- **rg** (ripgrep)
- **man** and **col**
- Optional: **less** / **more**, **fzf-tmux**, **timeout**
### Plain `source`
```zsh
# ~/.zshrc: use your real clone path
source ~/src/RTFM/rtfm.plugin.zsh
# If fzf --zsh or compinit rebinds Tab, put this last:
fzf_rtfm_rebind_tab
```
### Oh My Zsh
```bash
git clone https://github.com/hoolies/RTFM.git \
${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/rtfm
```
Add `rtfm` to `plugins=(…)` in `~/.zshrc`.
### Zinit
```zsh
zinit ice wait lucid
zinit snippet /FULL/PATH/TO/RTFM/fzf-man-opts.zsh
```
---
## Keys
### At the prompt
| Key | Action |
|-----|--------|
| **Tab** | Complete command, then options / arguments / paths |
### Inside the picker
| Key | Action |
|-----|--------|
| **Type** | Fuzzy-filter the token column |
| **↑ ↓** / **Ctrl-j** / **Ctrl-k** | Move |
| **← →** / **Ctrl-h** / **Ctrl-l** | Scroll preview |
| **Tab** | Zoom into a non-empty directory, or insert file/option/empty-dir and stay open |
| **Enter** | Insert and return to the shell (or apply a Ctrl-f filter while composing) |
| **Esc** | Abort, or cancel / clear Ctrl-f |
| **Alt-.** | Toggle hidden names (dotfiles) |
| **Ctrl-f** | Case-sensitive regex over options (then **n** / **N**\|**p**) |
| **?** | Help (**q** to close) |
---
## Environment
| Variable | Meaning |
|----------|---------|
| `FZF_RTFM_USE_TMUX` | Non-zero in tmux → use `fzf-tmux` (helps when typing fails in the pane) |
| `FZF_RTFM_TMUX_OPTS` | Extra `fzf-tmux` args; default `-d 90%` |
| `FZF_RTFM_HIST_DEPTH` | History lines for command-picker ranking (default `4000`) |
| `FZF_RTFM_NO_PATH_SCHEME` | `1` → skip `--scheme path` (very old fzf) |
| `SVDIR` | Runit services for `sv` (default `/service`, else `/var/service`) |
The picker does not read `FZF_DEFAULT_OPTS`. A bind there (for example Enter running a command) would break “Enter only inserts”.
---
## Credits
Sources, tools, and inspiration: **[ATTRIBUTIONS.md](ATTRIBUTIONS.md)**.
License: **[MIT](LICENSE)**.
---
## Nitty Griddy
Technical reference for contributors and anyone debugging the widget.
### Repository layout
| Path | Role |
|------|------|
| `fzf-man-opts.zsh` | Full implementation |
| `rtfm.plugin.zsh` / `rtfm` | Loaders |
| `tests/rtfm-unit.zsh` | Non-interactive unit tests |
| `assets/fuzzy-manual.png` | Logo |
| `ATTRIBUTIONS.md` | Credits |
| `LICENSE` | MIT |
### Feature details
**Command completion**
- Empty line or incomplete first word → PATH + builtins.
- One prefix match → insert `name ` and open the options picker in the same Tab.
- Several matches → fzf ranked by history frequency, then name.
- Path-shaped first token (`./`, `../`, `/…`, `~/…`) → directory listing, not PATH.
**Options and arguments**
- Prefer man (including `cmd-sub` topics); fall back to `--help` / `-h`.
- Token starting with `-` → options only (no file mix-in).
- Files mixed in when SYNOPSIS/usage suggests `FILE` / `PATH` / `` / …
- Cwd listings include a top-level `/` entry.
**Path browsing**
- Always depth 1.
- After Tab-zoom, display **basenames**; insert/preview use the full path.
- Typed directory prefix → directories only, no man rows.
- `/` skips `/proc`, `/sys`, `/dev`, `/run`.
- No `..` entries. Hidden names on by default (**Alt-.** toggles).
**Special parsers**
| Command | Behavior |
|---------|----------|
| `ip` | Objects from `ip(8)`; then `ip-