# qpath
Quick Path — register, list, and maintain frequently used file and directory paths.
`qpath` keeps a registry of path abbreviations in TOML files, renders them through a small template language, drops entries whose paths do not exist, and prints the rest for shell and editor integration. It is designed as a data source: shell widgets and editor commands read its output instead of maintaining their own path lists.
## Installation
```console
% cargo install qpath --locked
```
Or from a checkout:
```console
% cargo install --path . --locked
```
## Configuration
Definitions are loaded from, in this order:
```text
~/.config/qpath/paths.toml # default target for editing commands
~/.config/qpath/paths.d/*.toml # sorted by file name
```
The same abbreviation may be defined more than once; like `git config`, the last definition in load order wins (among entries that pass type and existence filtering). This lets a later file under `paths.d/` override an entry from `paths.toml`.
Each `[[path]]` table defines one entry:
```toml
[[path]]
abbr = "gh"
path = "~/src/github.com/"
desc = "GitHub"
[[path]]
abbr = "i"
path = "~/.emacs.d/init.el"
type = "file"
```
`abbr` and `path` are required. When `desc` is missing, the path itself is used as the description in output. `type` is one of `directory` (`dir`, `d`), `file` (`f`), or `existent` (`exist`, `e`); when omitted, a path ending in `/` is a `directory` and anything else is `existent`. A leading `~/` or `~user/` is expanded. Entries whose rendered path does not exist, or does not match their type, are silently skipped.
### Templates
Paths are rendered with a Jinja2-like template language ([MiniJinja](https://github.com/mitsuhiko/minijinja)). Built-in variables: `home`, `config_home`, `data_home`, `cache_home`, `state_home`, `os`, and `arch`. The `*_home` variables are platform aware — on macOS `config_home` is `~/Library/Application Support`, elsewhere `${XDG_CONFIG_HOME:-~/.config}`.
Custom variables can be defined in a `[vars]` table in any definition file:
```toml
[vars]
emacs_dir = "~/.emacs.d/"
[[path]]
abbr = "ed"
path = "{{ emacs_dir }}"
desc = "Emacs"
```
Available filters:
- `glob` — Expand a glob pattern into an array of matching paths. Brace alternations such as `{a,b}` are expanded first and each alternative is globbed independently; matches are sorted lexically within each alternative and concatenated. A trailing `/` restricts matches to directories.
- `vsort` — Sort an array in version-fragment order, so `python3.9` sorts before `python3.10` and `v29.9` before `v29.10`. Pipe `glob` through this to pick the newest versioned match with `last`.
- `shell` — Run a command with `sh -c` and return its standard output, trailing newlines stripped like `$(...)`. `cache_ttl=SECONDS` caches the output per command under `~/.cache/qpath/shell/`; failures are never cached.
- MiniJinja built-ins such as `first`, `last`, `sort`, `reverse`, and `join`.
```toml
[[path]]
abbr = "sp"
path = "{{ '/opt/homebrew/lib/python3.[0-9]*/site-packages/' | glob | vsort | last }}"
desc = "Python site-packages"
[[path]]
abbr = "p"
path = "{{ '~/{.Debfile,.Brewfile}' | glob | first }}"
desc = "Package list"
[[path]]
abbr = "brew"
path = "{{ 'brew --prefix' | shell(cache_ttl=86400) }}/etc/"
desc = "Homebrew etc"
```
## Usage
### Listing
```console
% qpath ls
as ~/Library/Application Support/ /Users/you/Library/Application Support/ ~/Library/Application\ Support/
gh GitHub /Users/you/src/github.com/ ~/src/github.com/
```
`qpath ls [--type TYPE] [--format tsv|json] [--expand]` lists entries as `abbr`, `desc`, `path`, `shell_path` TSV columns or a JSON array. `path` is the raw absolute path, ready to pass to file APIs. `shell_path` is quoted for direct insertion into a shell command line, with a leading `~/` left unquoted so it stays expandable. When `desc` is missing, the `~/`-shortened path is shown instead. `--type` filters by what the path is on disk (default `existent`), and `--expand` makes `desc` and `shell_path` use absolute paths instead of shortening under `~/`. `list` is an alias for `ls`.
`qpath show <abbr> [--type TYPE] [--format tsv|json] [--expand]` prints the single entry whose abbreviation matches exactly, in the same columns as `ls`, and errors if no such entry exists (or is filtered out by `--type`).
### Editing
```console
% qpath add gh ~/src/github.com/ --desc GitHub
% qpath update gh ~/src/gitlab.com/
% qpath rename gh hub
% qpath rm hub
% qpath format
```
Editing commands target `~/.config/qpath/paths.toml` by default; `--file` selects another definition file. They preserve comments and formatting, and keep entries sorted (`--sort-by abbr|path`). `qpath add` always appends an entry, so the same abbreviation may appear more than once; `--overwrite` instead replaces the last existing entry in the target file (or appends if there is none), preserving fields not given on the command line. `qpath update <abbr> [path]` updates the last existing entry in the target file the same way, but errors if the abbreviation is not present there; the path is optional, so it can change only `--desc` or `--type`. Both commands only edit the target file, and warn (add) or point at the other file in the error (update) when the same abbreviation also lives elsewhere. `qpath format` re-sorts a file edited by hand and tidies its whitespace (trailing spaces, repeated blank lines). `remove` is an alias for `rm`, and `fmt` for `format`.
### Cache
```console
% qpath cache clear [shell]
```
Removes cached data under `~/.cache/qpath/`; with `shell`, only the shell filter's command cache.
### zsh integration
```sh
insert-qpath() {
local sel
sel=$(qpath ls --type directory | fzf --delimiter='\t' --with-nth=1,2 --bind 'one:accept' --query='^' | cut -f4) || return
LBUFFER+=$sel
}
zle -N insert-qpath
bindkey '^Xq' insert-qpath
```
## Author
Copyright (c) 2026 Akinori Musha.
Licensed under the MIT license. See `LICENSE` for details.
Visit the [GitHub Repository](https://github.com/knu/qpath) for the latest information.