EmacsCompletionForcingCategories

By admin
Minibuffer completion is the most common type of completion in

Emacs
PRODUCT

and it’s invoked in all sorts of situations and thus to complete all sorts of different things. As part of this,

Emacs
PRODUCT

completion has the concept of a completion category that can be used to customize aspects of completion in both basic

Emacs
ORG

(eg, also) and in and for

third
ORDINAL

party packages like

vertico
GPE

(eg) and orderless. My personal experience is that this customization can be very useful to make me happy with

third
ORDINAL

party packages; the default vertico experience is not what I want in some types of frequently used completions.

(

Vertico
ORG

can customize things on a per-command basis, but this can get tedious if you have a bunch of commands that all complete the same sort of thing and you want to adjust in the same way. And you can’t adjust

Emacs
PRODUCT

completion styles on a per-command basis in stock

Emacs
PRODUCT

.)

In

Emacs
PRODUCT

Lisp code you may write, the most straightforward way to do minibuffer completion is using completing-read with a big list of all of your completion choices. Often this is the most useful form as well, partly because it allows extensions like orderless to act at their most powerful, with a full view of all possible completion candidates. Unfortunately, when you invoke completing-read this way, as far as I know there is no normal way to provide a completion category. You can only provide a completion category through programmed completion, where you provide a completion function instead of a collection of choices and

one
CARDINAL

of the things your completion function does is return completion metadata, including the category.

If we want to force the completion category anyway, the way I’ve found to do this (researched from marginalia) is to hook into the internals of the completion functions with advice-add. Specifically we need to hook into completion-metadata-get , which is what completion uses to extract a particular metadata property from a (nominal) blob of metadata:

(defvar cks/completion-category nil "Forced completion category.") (defun cks/completion-force-category (metadata prop) (if (and cks/completion-category (eq prop ‘category)) cks/completion-category)) (advice-add ‘completion-metadata-get :before-until ‘cks/completion-force-category) ;; used this way: (defun cks/some-completion (msg) (let ((cks/completion-category ‘my-special-category)) (completing-read msg list-of-stuff …)))

(I’m not sure where I picked up ‘<name>/’ symbol name prefixes as a way to avoid name conflicts, or if it’s the accepted

Emacs
ORG

style

these days
DATE

.)

As is traditional, we dynamically set the value we want for our (forced) completion category to something and then invoke completing-read. The dynamically scoped value will pass through to our added advice and be returned as the category value (overriding any category that’s already in the metadata, because that’s easier to code).

Possibly there’s already a standard

Emacs
PRODUCT

Lisp way of providing or setting the category of a basic completing-read. If not, my personal view is that there should be (and maybe there will be someday). Completion categories are neat and useful, so it should be easy to use them. In the mean time, well, this approach works for me in

Emacs 29.1
LAW

.

Sometimes you may have a more sophisticated completion environment where there’s already a special completion function and some existing elisp code that calls it, but the special completion function doesn’t implement providing metadata to the completion system. In that case you can advice-add the special completion function, which is simpler and normally doesn’t need a special variable:

(defun cks/mh-folder-complete-note (name predicate flag) (if (eq flag ‘metadata) ‘(metadata (category . mh-e-folder)) nil)) (advice-add ‘mh-folder-completion-function :before-until ‘cks/mh-folder-complete-note)

(You don’t need to explicitly return nil, but this particular bit of code was written before I had internalized some bits of standard Lisp behavior.)

PS: In real usage these functions should have docstrings and perhaps comments, but I’ve omitted them for space reasons.

PPS: Since I looked up the code, a possible alternate approach would be to advice-add the completion-metadata function, which is what completion calls to obtain the metadata from a completion function in programmed completion. Getting the format right is up to you; see the Lisp code for completion-metadata .

Sidebar: marginalia and its category overrides

In theory marginalia has its own system for setting completion categories based on various things, including the current command. ‘Command’ here is

Emacs
PRODUCT

jargon for the interactive function that was directly invoked either through a key binding or through M-x (and specifically marginalia bases this on the value of this-command , which is probably obvious to experienced

Emacs
ORG

people). Unfortunately, I was unable to get this marginalia feature to affect the completion category as recognized by

vertico
GPE

, so I eventually resorted to brute force (which did work with

vertico
GPE

).

Also, this way I don’t have to maintain and update a list of all of my commands that call my core completion function. I can just modify the core completion function itself and automatically cover any future use of it I add as I think of more possibilities.