reddit / emacs
Fuzzy Finding with Emacs Instead of fzf
mickeyp · 2022-07-25 13:06
mickeyp · 2022-07-25 13:08
I discovered `fzf` recently and thought it was great that more people get to experience what a good completion framework can do to your productivity. So I figured I'd write about how you can sidestep`fzf` entirely and just use Emacs.
Keen to hear about cool use cases for `fzf` (and `ezf`!)
[deleted] · 2022-07-25 14:03
I have no time to read it now but it looks very interesting at first glance so I bookmarked your link and thank you in advance for sharing again your goodies.
I'm a big fan of `fzf`, rarely use it in Emacs but can't live without it in the shell/terminal. I have made some scripts to bookmark/edit/etc commands, handle my cheat sheet collections (both essential for a blond like me), browse manpages and /usr/share/doc, and more, all with live previews. For doing more or less the same within Emacs I mostly use `Helm` and its preview capability, or `Ivy` instead of `fzf`.
fk00 · 2022-07-25 16:44
Can you share few of these code snippets, just to kind of get the high level of understanding of your approach?
BeetleB · 2022-07-25 16:59
I can't tell if your solution does this, but: One of the main strengths of fzf is it is "live" - you don't need to wait for the program to end and it dynamically updates the list.
As an example, if you run fd (or find) and pipe to fzf, you'll get the selection immediately, and it dynamically updates the list as fd returns more results. Equally important: If you had typed some text to filter, and a later update matches your text better, it will make that the selected one.
Does your solution do both of these?
trae · 2022-07-25 17:03
Neat!
There's also [fzf.el](https://github.com/bling/fzf.el).
FluidDr1nker · 2022-07-25 17:10
I'm surprised no one has yet created a completion interface on top fzf, a la consult.
mickeyp · 2022-07-25 17:59
`cat` will block until EOF at which point all the results are available. Having said that, you can modify the script and elisp to read from an fd instead. Or, use bash's `read` to read and append a line at a time to a file then use (say) helm to asynchronously re-read from the file as you type. Patches welcome!
BeetleB · 2022-07-25 18:11
Thanks. Will submit patches if I ever implement this solution. For me and many fzf users, this is necessary. For example, I often run fzf with fd on a directory with a large number of subdirectories/files (e.g. my home directory). A blocking solution is not workable.
mickeyp · 2022-07-25 18:44
I don't know if I really understand:
- If non-blocking is essential, and you're looking for something, then how do you know when to stop except when you've exhausted the file descriptor? It's quite possible the thing(s) you want are at the end.
- And if you do know what you're looking for, why not query for that explicitly to begin with?
jimehgeek · 2022-07-25 20:05
Someone kinda has with [`fussy`](https://github.com/jojojames/fussy). It supports multiple filtering and sorting techniques, including fzf compiled as a dynamic module for emacs, flx, flx-rs, and more.
For reference, my personal setup for fussy is here: https://github.com/jimeh/.emacs.d/blob/master/modules/editor/siren-fussy.el
BeetleB · 2022-07-25 20:11
Let me give you a concrete example. In my shell, I've bound a keystroke to run fd + fzf on the current directory, and I start typing so it'll filter and select the file I'm looking for, which could be several levels below where I am. I do this either because I'm too lazy to type the full path, or because I'm not sure of the full path.
Sometimes I even do this from the home directory.
It works because fd will often arrive at the file I'm looking for fairly early. I may have 1M files under home, but the one I'm looking for may be in the first 5% it encounters.
The equivalent in Emacs would be: I know there's a file deep down in this directory that I want to open. I recall only a portion of the file name. Let's say I know it has "cat" and "dog" in its name, but not which comes first And somewhere in the full path is a directory with "blah" in its name. I'd like Emacs to asynchronously start populating the completion list with all the files it finds as it traverses. I type in "cat dog blah", and it continually updates the candidates as it finds more files.
Waiting for fd to find *all* the files before I can start filtering is wasteful, and will take longer.
It's painful to construct an fd query that will get it to me.
I don't know up front whether typing "cat dog blah" will be sufficient to narrow it to one file, so I need the filtering to be interactive. However, for Emacs purposes, it may be good enough.
I do this almost daily in the shell. It's really handy.
[deleted] · 2022-07-25 20:39
Here a simple snippet for selecting & previewing man pages with `fzf`. Note that I use `bat` instead of `cat`.
manbrowse () {
# Select/preview a manpage with FZF. Accepts optional argument.
MANPAGER="sh -c 'col -bx | bat -l man -p --paging always'" man -k . | \
fzf --reverse -q "$1" --prompt='man> ' \
--ansi --inline-info \
--bind 'ctrl-p:toggle-preview' \
--bind 'alt-u:preview-page-up,alt-p:preview-page-down' \
--bind 'alt-i:preview-up,alt-o:preview-down' \
--bind 'alt-d:kill-line,ctrl-k:kill-word' \
--header 'C-p preview, A-uiop scrolls, ENTER open manpage, ESC cancel' \
--preview $'echo {} | tr -d \'()\' | \
awk \'{printf "%s ", $2} {print $1}\' | xargs -r man | col -bx | \
bat -l man -p --color always' | \
tr -d '()' | awk '{printf "%s ", $2} {print $1}' | xargs -r man
}
My script to bookmark commands is inspired by [https://github.com/pindexis/marker](https://github.com/pindexis/marker). I removed some dependencies but I've seen a better job done somewhere. It is also too big to post here.
deaddyfreddy · 2022-07-25 21:47
doesn't `M-x man` work good enough?
[deleted] · 2022-07-25 21:53
`M-x man` is not the same as fuzzy searching in all man pages, with scrollable preview. I use the shell script above in a terminal, not in Emacs.
Fun_Republic_1882 · 2022-07-25 23:47
Cool. Doom Emacs already has this and many other useful features. So Doom is nice to look for inspiration as well
w0ntfix · 2022-07-26 03:44
Thanks for sharing Mickey!
I am a big fan of shell mode, and have a [similar hack](https://github.com/neeasade/emacs.d/blob/master/scripts/fzf) that I use to shadow fzf (but limited to a single selection, and blocking).
I shadow other oft-used terminal commands, redirecting ones that expect a terminal/tui to a real terminal: [code snippet](https://github.com/neeasade/dotfiles/blob/c3d8843665cfe21a21d46cc5c3fd838d401529e9/shell/.sh.d/environment_emacs#L33-L65)
w0ntfix · 2022-07-26 03:57
I have a similar flow. FYI bling's [fzf wrapper](https://github.com/bling/fzf.el/) provides `fzf-with-command` to work around this limitation
mickeyp · 2022-07-26 05:27
No, that is true, but this is very close:
(let ((Man-switches "-k"))
(call-interactively 'man))
You'll get a buffer with all the apropos-like matches. You can hit RET on them to open the actual man page. And, of course, the inimitable helm has `helm-man-woman` that does all it all automatically :)
mickeyp · 2022-07-26 05:34
Thanks for clarifying. That makes more sense, but whenever I need to look for files I'd use GNU `locate` and `updatedb` to refresh the index.
Those tools are literally made to search and filter files on file systems.
You can have multiple database files, if you like, with one for each 'area'. Helm can already talk to locate with `helm-locate` and of course there's a million command line switches to `locate` also.
deaddyfreddy · 2022-07-26 08:22
Besides that, it's a breeze to work with texts in Emacs, comparing to basic editing facilities of a terminal emulator.
karthink · 2022-07-26 09:19
`consult-man` (part of the Consult package) can find man pages with previews using fuzzy matching and an async process. It works quite well.
The consult async command system is the closest thing to fzf in Emacs that I know of. In some ways it's better, but it's not as trivially composable as fzf is with pipes.
BeetleB · 2022-07-26 16:28
That's true, but:
1. I use Emacs on my Windows laptop. Will locate and updatedb work there? fzf and fd do.
2. I don't want to learn all the different ways to search (with all the various command line options). That's the beauty of fzf + fd (or fzf + whatever tool). Can you, without consulting the man page, construct the command to find a file that has "cat" and "dog" in its name (order unknown), and somewhere in the tree there is a directory with "blah" in its name? The nice thing about fzf it it is one consistent search interface that you can use with almost *any* tool.
3. For the shell it's convenient to bind to a keystroke. Consider your TAB autocomplete usage in a typical shell. What if it went away and someone told you to use fd, locate or any other tool and pipe into the command you want. It's a pain, right? Likewise I'd like to have the generic capability to have fzf like behavior in Emacs.
mickeyp · 2022-07-26 17:37
You should use what ever makes you happy and productive. But scanning millions of files which presumably are mostly static is a solved problem. That's why I probed the need for probabilistically hoping you'll find the one file you need. But everyone works in different ways; that's totally cool!
BeetleB · 2022-07-26 18:03
Compatibility between Windows and Linux is big for me. I use the same config on my Linux and Windows machines.
r1ss0le · 2022-07-27 12:54
I also made a similar tool, but relied on image dumping rather than the emacs server https://github.com/russell/fzel
krystah · 2022-08-27 17:03
I'm in your boat. Being able to find a file anywhere, without knowing the path, has been central to my workflow for years. I've dabbled with Emacs several times, but since I've yet to find an approach that mimics this, I usually end up returning to fzf/fd/rg since it feels like Emacs is getting in the way of my work.
If `locate` can solve this problem for me (find a file, anywhere on the system, near instantaneously, without knowing anything about the path), I'll give it another shot