I must admit, today I sifted through titles to find one that could fit what I did, instead of tailoring what I did around a randomly-selected title. However, I think this is better than the last two Sundays, when I missed writing anything altogether. But since I wanted to showcase something I’ve written, I’m calling today’s post Vanity jar. Nothing with the words “fuzz” or “choice” or “menu” were in my moonphoto list.
I really like dmenu,
which is a simple menu that takes input from stdin
, allows
the user to select one by typing, Tabbing or pointing, and prints it to
stdout
, to be processed by another program in the pipeline.
Many people use it as a program launcher, but it can be used for just
about anything.
I also really like fzf, which is a fuzzy finder – that is, a program similar to dmenu in that it interactively narrows a large number of items to one selection, but it uses fuzzy matching, which means it allows the user to type non-contiguous letters of what they’re looking for.
I wanted to set up fzf like dmenu,1 but since it’s a
commandline program figuring out stdin
and
stdout
was a challenge. I couldn’t figure out how to pipe
text straight into a terminal, so I used temporary files.
The script as it stands uses the st terminal in addition to fzf. It could easily be changed to use another terminal, however.
FMENU
#!/usr/bin/env bash
PROG="fmenu"
VERSION=0.0.1
version()
{
printf '%s v%s (c) Case Duckworth' $PROG $VERSION
exit 0
}
usage()
{
if [ $# -ge 2 ]; then
printf '%s: %s\n' "$PROG" "$2" >&2
fi
cat >&2 <<-ENDOFUSAGE
$PROG: a dmenu clone using st and fzf
usage: $PROG [-g GEOMETRY] [-t NAME] [-i] [-f FZF_OPTION...]
: $PROG [-h] [-v]
st(1)-tweaking options:
-g GEOMETRY: set terminal's geometry.
-t NAME: set fmenu's window title and name.
-i: see st(1)'s "-i" option.
fzf(1)-tweaking options:
-f FZF_OPTION: add an option to fzf(1)'s command line.
: it should be a long option,
: e.g. "--exact", "--color=light", "+i".
: see fzf(1) for details.
-h: show this help message.
-v: display version information.
see also st(1), fzf(1).
ENDOFUSAGE
exit "${1:-0}"
}
setup()
{
IN=$(mktemp)
OUT=$(mktemp)
if ! ST="$(command -v st)"; then
printf '%s: st required\n' $PROG >&2
exit 2
fi
if ! FZF="$(command -v fzf)"; then
printf '%s: fzf required\n' $PROG >&2
exit 2
else
FZF="${FZF} --layout=reverse"
fi
GEOMETRY="60x18"
WNAME="fmenu"
}
cleanup()
{
rm -f "$IN" "$OUT"
exit
}
main()
{
cat > "$IN"
$ST -t "$WNAME" -n "$WNAME" \
-g "$GEOMETRY" \
-e sh -c "${FZF} <$IN >$OUT"
cat "$OUT"
}
trap cleanup EXIT INT
setup
while getopts ':hvg:t:f:i' opt; do
case "$opt" in
h) usage ;;
v) version ;;
g) GEOMETRY="$OPTARG" ;;
t) WNAME="$OPTARG" ;;
f) FZF="${FZF} $OPTARG" ;;
# XXX: this doesn't work in dwm if there's a rule for 'fmenu'
# windows. I tried changing the WNAME too but that didn't work.
i) ST="${ST} -i" ;;
\?) usage 1 "Bad option: \"-$OPTARG\"" ;;
:) usage 1 "Argument \"-$OPTARG\" needs an argument" ;;
esac
done
shift $((OPTIND - 1))
main "$@"
cleanup
(One of these days, I’m going to release all the scripts I’ve written in a git repo somewhere. But today is not that day.)
Now that I think about it, I should also include options to make fmenu a drop-in dmenu replacement. But that, too, is for another day.