This package provides an asynchronous fuzzy finder similar to the fzf
command-line fuzzy finder, written in pure Elisp. A producer process is started
in the background, e.g., find
or grep
. The output produced by this process is
filtered by an external asynchronous Emacs process. The Emacs UI always stays
responsive since the work is off-loaded to other processes. The results are
presented in the minibuffer using Consult, which allows to quickly select from
the available items.
The package is available on MELPA and can be installed using the Emacs package
manager. If files should not be automatically previewed, a manual preview key
should be set for affe-grep
.
(use-package affe
:config
;; Manual preview key for `affe-grep'
(consult-customize affe-grep :preview-key (kbd "M-.")))
The default regular expression transformation of Consult is limited. It is
recommended to configure Orderless as affe-regexp-compiler
in Consult.
;; -*- lexical-binding: t -*-
(defun affe-orderless-regexp-compiler (input _type _ignorecase)
(setq input (orderless-pattern-compiler input))
(cons input (lambda (str) (orderless--highlight input str))))
(setq affe-regexp-compiler #'affe-orderless-regexp-compiler)
Affe requires the rg
(“ripgrep”) command line program to be available for best
performance. If rg
is not available, the usual Posix find
and grep
utilities are
used instead. The producer processes can be customized by adjusting the
variables affe-find-command
and affe-grep-command
.
affe-grep
: Filters the content of all text files in the current directory, similar toconsult-grep
.affe-find
: Filters the file paths of all files in the current directory, similar toconsult-find
.
Affe depends on Consult and works best with the Vertico and the Mct completion UIs.
- Consult: Useful search and navigation commands (Dependency of Affe).
- Marginalia: File annotations in the minibuffer.
- Embark: Minibuffer actions on files.
- Orderless: Advanced completion style, can be plugged together with Affe.
- Vertico or Mct: Vertical completion systems
The Affe frontend transforms the input string to a list of regular expressions
by calling the affe-regexp-function
. The regular expressions are passed to the
Affe backend via the emacsclient
protocol. The backend controls a producer
process, which generates lines of text. The lines are filtered using the regular
expressions submitted by the frontend. For performance reasons,
all-completions
is used for the filtering. The backend returns only a limited
amount of matching candidates, hopefully the most plausible ones. The frontend
calls the affe-highlight-function
on the returned matches, to highlight the
input pattern.
Affe uses a more primitive matching technique than fzf
, which uses the
Smith-Waterman algorithm. Affe does not perform any ranking or sorting; it
matches the lines in the order returned by the producer process against a list
of regular expressions. On the upside, this allows plugging Affe together with
the Orderless completion style, which can give a consistent fuzzy filtering
experience across all Emacs commands, including synchronous and asynchronous
commands.
As possible enhancement of Affe, one could implement alphabetical and sorting by length in the backend. By using a bucket sorting technique the sorting complexity will stay sufficiently linear such that the performance impact should be acceptable. However implementing a scoring-based sorting is probably not feasible since this requires heavier computations in Elisp. But maybe nativecomp Emacs is a game changer here?
- consult-grep/consult-find: Perform the filtering in the frontend.
- counsel-fzf: Ivy-specific, restarts find+fzf when updating the input pattern.
- fzf.el: Runs the fzf command line program in an Emacs terminal
- fuzzy-finder.el: Similar to fzf.el