A while back I read a post by Ömür Özkir about using fzf as a replacement for dmenu. For those that're unfamiliar.
dmenu is an application dispatcher. It pops up with a bunch of options for you to choose and then does something with them. It's commonly configured to pop up with a list of the programs installed on your system and then starts the program you select. Luke Smith made an excellent video about dmenu, I highly recommend checking it out.
fzf is a command line fuzzy finder. It takes a bunch of possible choices from stdin and outputs chosen ones to stdout. This isn't too dissimilair from dmenu except fzf can only be run from a terminal and it doesn't automatically make a window to show options like dmenu.
In Ömür's post he outlined how he used the users PATH
variable to find a
bunch available executables, pass them to fzf to have the user select one and then
started the chosen programs. He described it as a dmenu replacement and I really
liked his solution.
#Too Permissive
However I did find one minor issue with Ömür's implementation. Not all programs you can spawn from the command line are supposed to be run interactively.
Ömür's approach simply offered every executable in your PATH
. So some programs like
python
would crash immeadiately if you select them. It even offers innocuous shell
builtins such as [
or test
as candidates when there's no real chance you'll ever
want to use them in this context.
#Let's Fix That
So how do you discern interactive from non-interactive programs? How does my Desktop Environment do it? Well as it happens, unix like systems have adopted the XDG directory specification as a standard for configuring executables. So a reasonable way to shrink the number of available candidates would be to only offer programs that have associated .desktop entries.
It's not that straightforward however. The XDG spec also specifies how to spawn them programs. There's an entire section in the spec dedicated to special format codes used to construct the command line for a program. After some trial and error I finally got a fzf-dmenu that I'm happy with, so I think it's about time to share it with the world 😂.
#Side Notes
Unlike Ömür's solution, my implementation doesn't have any logic for spawning
terminals. He used a mixture of xdotool
and urxvt
for this, but I didn't think
that sort of logic belonged in the script itself. Mine can be used directly from an
open terminal and the only way to spawn it in a new terminal is to spawn the terminal
first and then run fzf-dmenu
.
Incidentally I've been using xbindkeys
for binding keys and I do have a
script for creating new terminals and then running programs in them. It
was partially adapted from Ömür's implementation here, except it's designed to work
with my terminal of choice (st
) instead of urxvt
.
My implementation also maintains the behaviour of Ömür's original solution (I.E.
offering every executable in your PATH
). You can toggle it by passing the -a
flag to fzf-dmenu
. It's retained partially for compatibility, but more so in case
you try to spawn an executable that doesn't have an associated .desktop file. I
recommend binding fzf-dmenu
and fzf-dmenu -a
to two complimentary keys in your
config. For example, I've bound them to S-[
and S-]
respectively.
#The Final Script
Another caveat of using fzf is that you can select multiple candidates at once. I'm
not sure whether dmenu offers this, but I really like being able to spawn multiple
related programs at once. I can start fzf-dmenu
, select my editor, my file browser,
my web browser and start them all at once.
#Future Improvements
There's still one minor issue with this implementation, spawning terminal apps (such
as vim). The XDG spec says you can attach a Terminal=true
section to your
.desktop
files to indicate that they should be run in a new terminal. When I get a
chance I'll add support for this to my script as well 😄.