Complement corfu, vertico, and completion-preview with prescient.el sorting
Prescient.el is a package that was popular during the era of ivy and helm. Nowadays, I don’t see it mentioned much because of the very popular corfu + vertico + marginalia + orderless combination which enhance built-in Emacs completion and overtaken ivy and helm in terms of popularity. (And it’s deserved! I use this combination, too.)1 But I’ve found prescient.el a noticeable convenience that complements this set of packages.
The reason is prescient.el’s sorting. Roughly, we can think of completion as having two halves: filtering and sorting. Filtering is what completion-styles does: among the generated completion candidates, which are shown to the user? Sorting is the order in which the filtered candidates are shown. One might think that orderless, which is filters candidates with its completion-style, also sorts candidates—but it doesn’t. As explained in the orderless README:
The prescient.el library also provides matching of space-separated components in any order. It offers a completion-style that can be used with Emacs’ default completion UI, Mct, Vertico or with Icomplete. Furthermore Ivy is supported. The components can be matched literally, as regexps, as initialisms or in the flex style (called “fuzzy” in prescient). Prescient does not offer the same flexibility as Orderless with its style dispatchers. However in addition to matching, Prescient supports sorting of candidates, while Orderless leaves that up to the candidate source and the completion UI.
As such, in the corfu + vertico + marginalia + orderless world, vertico (for minibuffer completions) and corfu (for in-inline completions) are responsible for sorting candidates.
The problem is this: although orderless brilliantly narrows down candidates, both vertico and corfu have somewhat naive sorting algorithms (see vertico-sort-function and corfu-sort-function). Vertico offers vertico-sort-history-alpha and vertico-sort-history-length-alpha, and corfu offers corfu-history-mode. In my limited experience, however, I give prescient.el’s sorting algorithm an edge to both. (YMMV—feel free to try them!) Oftentimes vertico and corfu will consistently show the candidate you have in mind later in the list. This tends to be the case when using certain common commands and certain completion-at-point-functions: I would common seek out certain candidates that orderless would place third or fourth in the list.
Prescient.el almost entirely solved this issue for me. Prescient.el offers a completion-style—this is its filtering functionality—but it also offers sorting functionality. The sorting functionality is what’s relevant here:
When sorting, the last few candidates you selected are displayed first, followed by the most frequently selected ones, and then the remaining candidates are sorted by length.
Prescient.el is the core package, and there are several auxiliary packages to integrate prescient with vertico, corfu, and others. We can enable prescient’s sorting for vertico and corfu (but not its filtering; I want to leave this to orderless) with something like the following:
|
|
The result is using orderless for completion filtering and prescient for completion sorting everywhere—for in-line completions with corfu and minibuffer completions with vertico. With this, among the candidates filtered by orderless, the most recent and common ones will be bumped up to the beginning. Try it out!
Bonus: integration with completion-preview-mode
Emacs 30.1 ships with the new completion-preview-mode. You can read about it in this blog post but also in the Emacs 30.1 news (i.e., C-u 30 C-h n
). Apparently, completion-preview-mode
has its own sorting function—which makes sense, since it isn’t hooked into either corfu nor vertico (which we set up to use prescient above). The relevant user option to modify its sorting is completion-preview-sort-function. So we just have to make sure that function matches prescient’s. We can do that like this:
|
|
But, if you also use corfu on top of completion-preview-mode, you’ll still notice a discrepancy between corfu’s first candidate and completion-preview’s candidate. They should be the same with the same sorting algorithm but they’re not! I’m not 100%, but I’m fairly confident that corfu-prescient keeps track of corfu-specific completion usage, which is separate from non-corfu-specific use. Consequently, the statistics used to sort candidates ends up different between corfu and prescient-completion-sort.
In any case, below is a simple fix that gets corfu and completion-preview on the same page:
|
|
Changelog
- Fixed several typos.
- Added clarity to several phrases.
- Added a note mentioning the new vertico-sort extension.
- Correction to explanation according to r/JDRiverRun’s comment.
- Added a note mentioning the new corfu-history extension.
In my opinion, each package is much more modular than the helm or ivy ecosystems while being more performant, integrated with existing Emacs functionality, and just as useful, if not more. Though a bit dated, I’ve previously written a big about how to configure this set of packages: Vertico, Marginalia, All-the-icons-completion, and Orderless. ↩︎
The Text Completion and Minibuffer UI series
This post is just one installation of the Text Completion and Minibuffer UI series. Below are all the posts in this series: