radian-software / selectrum

🔔 Better solution for incremental narrowing in Emacs.
MIT License
738 stars 33 forks source link

MELPA MELPA Stable

Selectrum... is replaced

Selectrum has been replaced by Vertico, a package which provides essentially the same features in a simpler way, and integrates more effectively with other packages.

The original author of Selectrum, @raxod502, now uses Vertico instead in his Emacs configuration, Radian.

There is a guide to migrate from Selectrum to Vertico.

Over time, we will improve this guide and ensure that it is possible to achieve feature parity for all existing configurations of Selectrum in the Vertico ecosystem (possibly with the use of one or more Vertico extension packages, of which there are many already).

It's always annoying to change from one thing to another, but we (the Selectrum development team) think the replacement will be a benefit to everyone pretty soon, because Vertico is a lot simpler and easier to maintain and integrate, meaning the end result is likely to be more robust and likely to stick around for longer.

Documentation

News feed

Selectrum is a better solution for incremental narrowing in Emacs, replacing Helm, Ivy, and Ido.

What is it?

Selectrum aims to provide a better completion UI using standard Emacs APIs. In essence it is an interface for selecting items from a list.

You can use it to run a command with M-x:

Picking from a
list of commands

You can use it to open a file with C-x C-f (find-file):

Navigating the
filesystem

Even TRAMP works great out of the box:

Using sudo via
TRAMP

You can switch buffers:

Switching to
another buffer

And every other command in Emacs is automatically enhanced, without the need for any configuration:

Finding
libraries, with load-path shadows

Installation

Selectrum is available as a package on MELPA. The easiest way to install this package is using straight.el:

(straight-use-package 'selectrum)

However, you may install using any other package manager if you prefer.

Usage

To enable Selectrum, simply add to your init-file:

(selectrum-mode +1)

Now all completion commands will automatically use Selectrum.

The focus of Selectrum is on providing an enhanced completion UI and compose with other packages which stay within the constraints of the standard Emacs API. Because of the modular approach there are several possible package combinations. Many tips and setup help for integration with other packages can be found in our wiki.

The default sorting method of Selectrum is simple and predictable. The candidates are first sorted by their history position, then by length and then alphabetically.

The default filtering of Selectrum uses the Emacs completion-styles. The default setting of the completion-styles variable is rather "basic" and you may want to adjust this variable for more advanced filtering. See for example the built-in substring and flex styles. Instead of using the built-in completion styles we recommended to use additional packages. Here we highlight two possible approaches for more advanced filtering and sorting: 1. Prescient and 2. Orderless.

Alternative 1: Prescient

Filtering and sorting can both be improved by installing the selectrum-prescient package from MELPA and adding the following to your init-file.

;; to make sorting and filtering more intelligent
(selectrum-prescient-mode +1)

;; to save your command history on disk, so the sorting gets more
;; intelligent over time
(prescient-persist-mode +1)
Alternative 2: Orderless

Another popular choice for filtering is to use the flexible orderless completion style.

(setq completion-styles '(orderless))

;; Persist history over Emacs restarts
(savehist-mode)

;; Optional performance optimization
;; by highlighting only the visible candidates.
(setq orderless-skip-highlighting (lambda () selectrum-is-active))
(setq selectrum-highlight-candidates-function #'orderless-highlight-matches)

The candidates are sorted using the default sorting method of Selectrum (by recency). The history is persisted using the Emacs built-in savehist-mode. Afterwards the candidates are filtered and highlighted using the completion-styles, in this case orderless.

In some cases you may want to consider to use Prescient on top of Orderless. Prescient can be used to provide frecency-based sorting (a combination of frequency and recency) and history persistence by adding the following.

(setq selectrum-prescient-enable-filtering nil)
(selectrum-prescient-mode +1)
(prescient-persist-mode +1)

User guide

The design philosophy of Selectrum is to be as simple as possible, because selecting an item from a list really doesn't have to be that complicated, and you don't have time to learn all the hottest tricks and keybindings for this. What this means is that Selectrum always prioritizes consistency, simplicity, and understandability over making optimal choices for workflow streamlining. The idea is that when things go wrong, you'll find it easy to understand what happened and how to fix it.

Keybindings

Selectrum respects your custom keybindings, so if you've bound next-line to M-* for some reason, then pressing M-* will select the next candidate. If you don't like the standard Selectrum bindings, you can change them in selectrum-minibuffer-map.

The keybindings listed above are the only ones changed from standard editing bindings. So, for example:

Sorting and filtering

The default sorting and filtering in Selectrum is quite simple and predictable. The method is similar to the one employed by Icomplete. Candidates are sorted first by history position (by recency), then by length and then alphabetically. Afterwards they are filtered and highlighted using the completion-styles. This default behavior is intended as a lowest common denominator that will definitely work.

It is strongly recommended that you customize completion-styles using Orderless or install Prescient as described before. It is also possible to supply your own sorting, filtering, and highlighting logic if you would like. For that, see the developer guide later in this documentation.

Independent of the sorting and filtering method, Selectrum adds two special features on top:

Case-sensitivity and other filter options should be configured via the used refinement function. The built-in completion-styles support the completion-ignore-case, read-file-name-completion-ignore-case and read-buffer-completion-ignore-case options.

Additional features

Customization

User options can be configured via M-x customize-group RET selectrum RET. Faces can be customized via M-x customize-group RET selectrum-faces RET.

Complementary extensions

For a fully fledged setup enabling additional features similar to those you find in Helm or Ivy, we recommend the following additional packages:

The above packages work well in combination and we are collaborating with each other to ensure an optimal experience while not introducing any hard dependencies. Our common denominator is the standard Emacs API.

For other possibly interesting packages, see our wiki which also contains configuration tips for many of these.

But what is it doing to my Emacs??

By inspecting the source code of selectrum-mode, you will see that Selectrum operates by setting a number of standard Emacs variables (completing-read-function, read-file-name-function, etc.) and installing advice on a number of standard functions (read-library-name, minibuffer-message, etc.).

If you object to these changes being made magically, you can make them yourself and refrain from enabling selectrum-mode. However, backwards compatibility is not guaranteed for this usage, so you will need to review the source code of selectrum-mode after each update of Selectrum.

The autoloads of Selectrum are set up so that you can enable selectrum-mode without actually loading Selectrum. It will only be loaded once you use some of its functionality in an interactive command.

If you want to enable selectrum-mode for everything except a few commands, you can advise those commands to temporarily deactivate selectrum-mode. For example, below is how one could disable Selectrum for org-set-tags-command. Note that such advice also affects recursive minibuffers.

(defun exclude-from-selectrum (orig-fun &rest args)
    (selectrum-mode -1)
    (apply orig-fun args)
    (selectrum-mode +1))

(advice-add 'org-set-tags-command :around #'exclude-from-selectrum)

News

We document changes for users in the CHANGELOG. To keep up with latest changes and features you can subscribe to the feed.

Developer guide

This section is intended for the authors of packages which integrate with Selectrum, or for end users who wish to customize the sorting and filtering behavior of Selectrum.

Usage of Selectrum

In normal usage, there should be no need to use any Selectrum-specific functions. Simply use completing-read and friends, and Selectrum will automatically enhance the experience if selectrum-mode is enabled.

Selectrum does expose some completion functions as part of its public API.

These functions are used as replacements for the standard completion functions when selectrum-mode is enabled. If you want to define your own commands using completion, it is recommended to use the standard completing-read API.

Sorting, filtering, and highlighting

Selectrum exposes a very simple API for sorting, filtering, and highlighting. Each of these three tasks is controlled by a separate user option:

For exact specifications of these functions, including whether or not the input list may be modified, please see their docstrings. This information is important, because if you make copies of the candidate list unnecessarily, there will be noticeable lag due to the slowness of Emacs' garbage collector.

Text properties

Selectrum allows changing the display of candidates within the constraints of the official API by make use of text properties of completion candidates. However it is preferable to use an annotation function (or affixation which is introduced in Emacs 28), see `(info "(elisp) Programmed Completion") to make the annotations work with any compliant completion framework. We also have some information about using annotations on the wiki.

The following text properties can be used, which may be applied to candidates using propertize:

Besides, we have:

Note that sorting, filtering, and highlighting is done on the standard values of candidates, before any of these text properties are handled.

Hooks

Selectrum provides two hooks for getting information about what candidates were selected. These are intended primarily for packages like prescient.el which want to record history statistics. The hooks are:

For more information, see their docstrings.

Variables

You can use the variable selectrum-is-active to check if the current minibuffer session is a Selectrum one.

To adjust session settings you can set the user option variables locally in minibuffer-with-setup-hook. Additionally the following variables can be used to adjust session behavior:

For more information, see the respective docstrings.

Contributor guide

Please see the contributor guide for my projects. We have some test scripts for testing minimal default configurations of common package combinations. You can run them using

cd test; ./run.sh <package-combo>.el

Technical points:

Caveats

Selectrum in comparison to other completion-systems

This section documents why I decided to write Selectrum instead of using any of the numerous existing solutions in Emacs.

I have not used many of these packages extensively. So, if you think I've overlooked an important part or I've written something mean or unfair, please feel free to contribute a correction.

See #23 for discussion.

Ido

Ido is a package for interactive selection that is included in Emacs by default. It's a great improvement on the default completing-read experience. However, I don't like how it displays candidates in a horizontal instead of a vertical manner. It feels less intuitive to me. Another key issue with Ido is that it hardly supports any commands out of the box (only buffers and files). There is an extension package ido-completing-read+ which adds support for the completing-read interface, but I have been told that even this package does not handle all the cases correctly.

There is a package ido-vertical-mode which makes Ido display candidates vertically instead of horizontally, but I suspect that the problems with completing-read non-compliance remain.

Helm

Helm is an installable package which provides an alternate vertical interface for candidate selection. It has the advantage of having very many features and a large number of packages which integrate with it. However, the problem with Helm for me is exactly that it has too many features. Upon opening a Helm menu, I am immediately confronted by numerous colors, diagnostics, options, and pieces of help text. It is too complicated for the problem I want solved. Of course, I am sure it is possible to customize Helm so that it is simpler in appearance. But that would take a long time and I would rather use a piece of software which was designed for the use case I have in mind. I also personally prefer using software that I have some hope of understanding, which ideally means that they don't provide a hugely complex array of features of which I only use one or two.

See #203.

Ivy

Ivy is a promising alternative to Selectrum. It is described as a minimal alternative to Helm which provides a simpler interface. The problem with Ivy is that its architecture and API have grown organically, and as a result the implementation is complex. Ivy was originally designed to be used as a backend to Swiper, a buffer search package that originally used Helm. When Ivy became a more general-purpose interactive selection package, more and more special cases were added to try to make various commands work properly. As a result, the ivy-read API is complex with around 20 arguments and multiple special cases for particular values. Numerous functions in Ivy, Counsel, and Swiper have special cases hardcoded into them to detect when they're being called from specific other functions in the other two packages.

The main differences between Selectrum and Ivy are:

Selectrum does not support features which break the completing-read API and works with every Emacs command with essentially no special cases, specifically because it focuses on doing the common case as well as possible.

Icomplete

Icomplete is the built-in Emacs package for interactive selection. It is basically the same as the standard completing-read framework, except that the available candidates are displayed in the minibuffer as you type. Unlike Selectrum, the candidates are displayed horizontally (by default). This can be changed by some manual configuration, including customizing icomplete-separator, although it is clear that this use case is not an intended one for Icomplete. A serious usability problem of Icomplete is that the way you select a candidate from lower down in the list is very unintuitive: you must "rotate" the entire set of candidates, whereupon the previous candidates become invisible since they have wrapped to the bottom of the list.

With sufficient configuration, it is likely possible to replicate a subset of the features of Selectrum using Icomplete. However, the documentation of Icomplete is basically nonexistent, and to achieve this configuration one must bend Icomplete rather severely away from the interaction model it is designed for. In other words, the configuration is not an enjoyable process, and the results will never be equivalent in user experience to a package that was designed for the desired interaction model in the first place. Selectrum, on the other hand, offers a well-tuned and snappy vertical completion interface that is robust and works out of the box.

There is a package which takes care of some of the manual labor of configuring Icomplete, called icomplete-vertical.

It is worth noting the new Fido mode which will be included in Emacs 27. It is basically a variation of Icomplete that behaves more like Ido. As such, Fido mode does not offer solutions to the problems outlined in the above sections.

On the upside, Icomplete is the most API compliant enhanced completion UI available. Selectrum also covers the most important aspects of the API and strives to achieve full compliance, as well. For the few edge cases left, see the Caveats section.

Vertico

Vertico is a new minimalistic completion system based on the Emacs default completion offering a similar UI as Selectrum. It uses a different implementation approach - it extends the default completion system in a similar way as Icomplete and is therefore fully compliant with all features of the completing-read API. Overall Vertico follows a similar philosophy as Selectrum, relying on default components and complementary packages. Many of the complementary packages, notably Consult, work well with both Selectrum and Vertico. Selectrum offers a flexible UI, e.g., it supports both a horizontal and a vertical display. Furthermore it provides Avy-style quick keys and display actions, to show the completions in a buffer. On the other hand, Vertico additionally supports cycling over candidates and provides more commands for grouping support.

Icicles

Icicles is a package somewhat like Helm, written by Drew Adams. Like other packages by Drew, Icicles is only available for manual download from EmacsWiki. It has been removed from MELPA due to community consensus that this distribution mechanism has unacceptable security risks, but Drew has declined to migrate to any other distribution mechanism.

Because of this situation, I have never attempted to use Icicles, and cannot comment on the package on the basis of its features. If you would like to submit a pull request explaining the advantages and/or disadvantages of Icicles versus Selectrum, we would appreciate it.

Snails

Snails describes itself as a "modern, easy-to-expand fuzzy-search framework". From the README, it seems to provide a similar vertical completion interface to Selectrum.

One problem with Snails is that, like Ivy, it goes the route of wrapping every possible command with a "backend" rather than using existing Emacs interfaces to handle all possible commands.

Sallet

Sallet describes itself as "a type of light spherical helmet", according to the repo description. However, it also appears to be another vertical completion interface. Although I haven't used Sallet extensively, here are some differences that I can note:

Raven

Raven is a little-known package for vertical completion. It looks quite similar to Selectrum, and seems pretty usable to me. The main difference is that Selectrum simply has a more fully-rounded set of features (such as candidate highlighting and a full find-file replacement). I suspect that these features have simply not yet been implemented.

Swiper

As discussed in the section on Ivy, Swiper is a buffer-search package that uses Ivy's interface and is coupled closely to the Ivy implementation.

Does Selectrum attempt to provide a replacement for Swiper in addition to Ivy and Counsel?

The answer is no - such functionality will not be part of Selectrum itself, but there are two alternatives available.