zipzap is a tool for tracking and jumping
to frequently-used directories.
It's easy to install and to integrate with your shell:
cargo install --locked zipzap # installs the binary
zipzap install # installs shell integrations
zipzap db import # imports an existing `z` database
# restart your shell to load shell integrations, then use `z` to jump around
More details are in the README; keep scrolling for the narrative backstory.
Still with me? Great!
This all started with – earlier this week – I made the mistake of running brew upgrade.
Every time I do, it's a roll of the dice as to what will break: maybe my carefully constructed Python environment will stop working, or my stack of neovim plugin will fail due to API changes
This time, it was my shell: fish had changed
how it handled colors,
so I got a spew of warnings every time I opened a new shell.
Years ago, I had set up
oh-my-fish, so there was an extra
layer of indirection here: my configuration loaded the omf code,
which did something with colors. I decided to tear it all out, copied the
prompt into my own
dotfiles,
deleted omf,
and called it a day.
Later that week, I typed z fidget into the shell. z is a utility for
jumping into directories based on "frecency" (a combination of frequency and
recency), so I expected this to jump to ~/code/fidget, where I spend a
lot of my time.
➜ ~ z
fish: Unknown command: z
It turns out that I had been using omf's
plugin-z integration, and now it was
gone.
I could just copy those plugin files into my own dotfiles, but my curiosity
had been piqued: how did z actually work? Here's
z.fish:
function z -d "jump around"
set -lx Z_SCRIPT_PATH (dirname (status -f))/../z/z.sh
# Start a Bash process, source z, run the _z function, and capture the working directory and exit status.
bash -c '
source $Z_SCRIPT_PATH
_z "$@" 2>&1
Z_STATUS=$?
echo "$PWD" >&2
exit $Z_STATUS
' bash $argv 2>| read -l Z_PWD
set -l Z_STATUS $status
# If z changed directories, reflect that in the current process.
if test $Z_PWD != $PWD
builtin cd $Z_PWD
end
return $Z_STATUS
end
Okay, so the fish function spawned a bash shell to invoke the _z function.
Let's keep going down the stack:
_z turns out to be
267 lines of bash, with the
heavy lifting delegated to an awk script.
Here's a chunk of the code, for flavor:
local echo fnd last list opt typ
while [ "$1" ]; do case "$1" in
--) while [ "$1" ]; do shift; fnd="$fnd${fnd:+ }$1";done;;
-*) opt=${1:1}; while [ "$opt" ]; do case ${opt:0:1} in
c) fnd="^$PWD $fnd";;
e) echo=1;;
h) echo "${_Z_CMD:-z} [-cehlrtx] args" >&2; return;;
l) list=1;;
r) typ="rank";;
t) typ="recent";;
x) \sed -i -e "\:^${PWD}|.*:d" "$datafile";;
esac; opt=${opt:1}; done;;
*) fnd="$fnd${fnd:+ }$1";;
esac; last=$1; [ "$#" -gt 0 ] && shift; done
[ "$fnd" -a "$fnd" != "^$PWD " ] || list=1
None of this is bad, per se, but I can't parse it easily, and at this point my foot was firmly stuck down the rabbit hole.
I spent the weekend poking around, and emerged with
zipzap, a single-binary replacement.
zipzap isn't particularly novel! There's z-rs,
zoxide, a
native fish port of z,
autojump,
and presumably dozens of others – it's a particularly appealing rabbit hole,
since you can knock out a customized solution in a day or two.
Still, it works for my use case, falling into the category of
"home-cooked apps" from Robin Sloan's essay.
It also gave me the chance to play with
rusqlite,
and another opportunity to build a
lovely hand-crafted CI suite.
All in all, time well spent.