Hacker News new | past | comments | ask | show | jobs | submit login
Fuzzy Finding with Emacs Instead of Fzf (masteringemacs.org)
138 points by signa11 5 months ago | hide | past | favorite | 50 comments

If you work on large repos at large companies using Vim or Emacs, my experience has been that most solutions seem to break down under the performance strain except for getting FZF in the mix. I've tried stock Telescope+neovim, Emacs+helm/ivy, etc.

I’ve been able to get decent performance from helm in large repos. I think the main thing is to get rg to walk the list of files instead of using the native elisp implementation.

I’ve found this article useful for understanding how to debug performance issues in Emacs: https://www.murilopereira.com/how-to-open-a-file-in-emacs/

Fantastic read, thank you for sharing!

Never tried other solutions, but fzf + vim + ripgrep handle everything I've worked with without any real performance issues even on 500K-1M line repos. Of course they're no longer instantaneous at those line counts, but still in the order of seconds.

Yeah that's the stack I was running as well. I think we had like 15 million LOC or more and it was useable.

At that point, I’d be project-scoping to individual submodules rather than the whole monolith. For something like projectile, that would mean dropping a .projectile in the submodule root so my “rip grip in project” functions are scoped to the submodule, then using VC to mark the root where I need to expensively search the whole project.

Could you explain more about this setup? I'm not familiar with "projectile". Is this https://github.com/bbatsov/projectile the same thing you're referring to?

Sounds interesting. What I've done recently is open my vim in the folder that contains all the organization's repos (the ones I've cloned) and just run ripgrep inside vim to find examples or references to whatever I've seeking. Seems performant enough even without doing anything except letting ripgrep ignore git-ignored stuff (default behavior of ripgrep).

Sure - projectile is one of the packages that convinced me to migrate from vim to emacs. It curates a notion of "project" and maintains a list of projects you visit. My workflow is based around a lot of using projectile-find-file, which prompts you for a known project, then dumps you into an completion interface to open a specific file.

Projectile works OOTB with .git directories, so if you visit a git-controlled dir it's added to your projects. You can similarly specify other directories as projects by putting a .projectile file in them, and the contents of the .projectile file act as an ignore list.

So the workflow is work/myMonolith is the git controlled root, while I have work/myMonolith/frontend/.projectile and work/myMonolith/backend/.projectile. So I can use the project-scoped find file, grep etc. to inherently narrow the search space to that module. When I'd want to globally search, I'd use projectile-find-file (or grep, or whatever) on the myMonolith root.

I tend to not like this extra step of managing submodules, but to each their own. Often enough I'm looking at many packages at once. I guess you have to globally gitignore the projectile file too to avoid committing them everywhere?

> I guess you have to globally gitignore the projectile file too to avoid committing them everywhere?

Indeed, in my global git ignore, same with other workflow-specific stuff that nobody else at $WORK uses

I considered projectile but then found that the built in project-find-file is enough for my puny webdev projects - they all have a .git by default and once in the dir, looking for files works it seems without any setup or config files. Does projectile have something I might like to have?

I don't know if there's a way to indicate a project with the built-in project.el without a VC root. The other feature i use regularly that I don't think exists in project.el is projectile-toggle-between-implementation-and-test, which does exactly what it sounds like. You can configure it by build tool and give it path regexes to substitute to specify how to find the corresponding Spec for a file.

I actually use a mix of both. Project.el being built in means it properly leverages the built-in completion stuff, which has a level of awareness for the types of things I search and gives appropriate icons and context information (see all-the-icons-completion[1] for details). I fibbed a little bit about my workflow - to switch projects and find a file I use this:

  (defun project-find-file-from-projectile-project ()
    (project-switch-project (completing-read "From which project?" projectile-known-projects)))
[1] https://github.com/iyefrat/all-the-icons-completion

Alternatively if the monorepo has a way to denote when you're in a new package (package.json, etc) you could key off that too?

I use project.el instead of projectile but yeah that is exactly what I do. It is only a couple of lines.


Give consult-rg with ripgrep and emacs a try. It's an excellent combination.

Another alternative is fzf.el [1] which wraps fzf.

[1] https://github.com/bling/fzf.el

and another one, from the author of consult/vertico/..., minad, is affe: https://github.com/minad/affe

I haven't C-x C-f'd much since installing that and adding keybindings to it. Amazing.

I always love reading these guides, and I will eventually give in and buy Mickey's book just as a quick peek into his overflowing mind.... but this article left me wanting for a fizzy completion engine in emacs.

There is the emacs-fzf package, icicles, and various other vertical completion frameworks, but I essentially want something that pretends to be fzf at the Emacs level, without having to spawn background shell scripts

Various vertical completion frameworks like vertico are essentially front-end UIs. You can choose underlying filtering and sorting/scoring algorithm by setting `completion-styles` variable. One popular choice here is the orderless package (which probably doesn't give "fuzzy" completion experience as most people call it I guess, orderless-flex does filtering but not heuristic based sorting like fzf does).

There are some options outlined in the readme of fussy[1], which is a wrapper around them. There are some pure elisp ones, and some written in native languages (including fzf itself) interfaced with dynamic module (so FFI rather than subprocess). Does that qualify as Emacs level?

[1] https://github.com/jojojames/fussy

Orderless is probably the way to go for a “pure emacs” solution - its selling point is that you can feed whatever function you want into the completion framework, so if you feed a fuzzy completer then you get fuzzy matching. It’s also part of a relatively new completion stack (that includes vertico, marginalia, and others) that leverages the inbuilt emacs features instead of replacing them.

Yeah, I'm currently using orderless with vertico for my "fuzzy" needs, but I genuinely do find ido completion much faster and more practical 90% of the time.

I have come to really think that a big problem in the open source world is the non-standardization of interfaces. Text over stdio and communicating processes is just not the greatest; if every program standardized on C ABI library protocols, and offered stdio and socket interfaces to them, then suddenly the landscape of possibilities in software composition would be way broader. Herding daemons and managing docker containers when we have the linker and dynamic libraries just doesn’t feel sane or sanitary to me, especially outside of the Linux shell. This is how we end up with 50 different implementations of syntax parsers, fuzzy-finding, API clients, etc.

> C ABI library protocols, and offered stdio and socket interfaces to them, then suddenly the landscape of possibilities in software composition would be way broader.

I think it would have reduced composition in practice. Part of what you get out of "text as the universal interface" is tools that never heard of each other being able to work together. Because there's only a couple types: string, maybe number depending on the context, list (strings separated by spaces or newlines) and record (single line of a csv/tsv file), things can be kinda coerced into working together.

You could get this with a type system... if you restrict yourself to at the most advanced List<String>, Map<String, String>, but you've lost ~80% of your type system value. As soon as anyone defines a custom struct, only tools that have heard of that type (which, in practice, end up being tools specifically made to work with it) can use it. Just using text with a few conventions is a really high utility point on the pareto curve for basically zero work.

I could see some benefit in getting a system defined Path/URL type, but you'd really need to have a culture of sticking to built in types, and a system that offers types to cover basically every use-case.

It's really hard for me to imagine a world being _less_ useful where a program like ls not only specifies the structure of its output in a way I can automatically read fields of without hand-parsing it, but also can be used by linking instead of having to run it in a subprocess whose input and output I have to manage, buffer, and so on and so forth.

> useful where a program like ls not only specifies the structure of its output in a way I can automatically read fields of without hand-parsing it

I don't think C ABI gets you this. Passing structs across ABI boundaries is kinda iffy, especially if you're using a different compiler than the program was originally compiled with. And lord knows what non-C programs would try to do.

I get it but all I can hear over and over is “arbitrary strings are better than a sound typesystem” and we all know that’s just not true.

> C ABI library protocols, and offered stdio and socket interfaces to them, then suddenly the landscape of possibilities in software composition would be way broader

You've just invented a rudimentary and worse version of COM. (There are advantages in not limiting yourself to low-level tooling like ptys/pipes/sockets.)


What is worse about native C FFI and structs-as-messages socket protocols as compared to the malebolge of Windows APIs?

COM, the interface, is not the same as the conglomeration of Windows APIs.

A web-dev example might be that CSS, the language design, is not the same as the ever-growing and inconsistent set of properties that the browser uses to style documents.

> I have come to really think that a big problem in the open source world is the non-standardization of interfaces. Text over stdio and communicating processes is just not the greatest…

We lost that battle in the late 70s/early 80s. It has nothing to do with the open source world.

Open source has everything to do with interoperability of software.

The problem of ad hoc nonstandard text interfaces, in both source-available and priority code far predated the gnu manifesto, much less anything that happened since.

That’s all.


Emacs completion frameworks are handy within our beloved editor but also well beyond. A recent post I wrote: https://xenodium.com/building-your-own-bookmark-launcher

M-x ask and ye shall receive:


Seems way more abstruse than just using fzf

fzf won't work in shell-mode as it does not handle curses-like apps. Thus, `ezf.el' avoids that problem by using Emacs as the fuzzy searching facility. To say nothing of the fact that this is a fine example to re-use for, similar, needs where you need to receive and send back information in an ordered manner.

(And helm is far more advanced than fzf is, as far as searching)

Alternatively, would fzf work in vterm?

Yes, of course. It'll work in EAT and M-x term too.

I prefer vipe + vim

To go big here; I did Emacs for a year or two then gave it up because -- despite being probably the most theoretically powerful -- there's just too much friction.

I don't know if it's been given a name, but this new wave of (usually Rust) shell tools that are great and seem obvious in retrospect (fzf, rg, etc) strongly feel like they're doing exactly right what Emacs has "failed badly" at.

edit: And now that I think about it, the "failure of Emacs" thing feels a lot not going the old "Unix way, do one thing and speak text" ideals?

I don't really understand the point being made. Virtually every popular unix-like command line tool integrates with the popular Emacs frontends. It's as trivial as going `consult-ripgrep` or `projectile ripgrep` and so on. There's significantly more friction for me in praying I remember a hundred terminal commands and tools than just having an actual UI.

I mean the other way around?

Living inside Emacs separates you from lots of "how everyone else does it" over time and is hard to integrate with "everything else" without TONS of work.

To avail yourself of the full power of Emacs, it demands more "difference from everything else?"

As a newcomer to Emacs in the past couple of months (my second try), I think I agree with you that it's a lot of work.

But for the Emacs fans who've had success, that work pays them back because they can bend the editor to an extremely bespoke workflow.

I'm unsure if it will work out for me long term but I can sort of see the appeal. No other tool even attempts to be what Emacs has become.

So yeah, it's different from every other approach I've tried. But there's a chance it will work for me. Jury's out, though, and I am only now realizing that I'm going to have to put more effort into hacking my workflow than with other things I've used (Kakoune, Neovim, sublime, etc)

I've been on both sides: emacs only and fish + cli toolig + neovim and there is certainly no "everyone" on the console side. "Everyone" lives in VSCode. People using console tooling do not invest less time into it compared to emacs folks and each setup is no less individual than an init.el

So, the "side" I mentioned here, I literally mean "ANYTHING not Emacs." Everything else you mentioned, even the neovims et al, do a far better job of "playing nicely with everything else" than Emacs.

I had a real "I wish magit was a TUI program and embeddable in Neovim, Emacs, your tmux sidepane" moment the other day.

No argument here, and I've been using emacs since around 1983. It's a hobby and a way of life. But like any way of life, it's for those who like that sort of thing.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact