Skip to content

Adding custom annotators or classifiers

IMPORTANT NOTICE FOR PACKAGE AUTHORS: The intention of the Marginalia package is to give the user means to overwrite completion categories and to add custom annotators for existing commands in their user configuration. Marginalia is a user facing package and is not intended to be used as a library. Therefore Marginalia does not expose library functions as part of its public API. If you add your own completion commands to your package we recommend to specify an ‘annotation-function’ or an ‘affixation-function’, avoiding the Marginalia dependency this way. The ‘annotation-function’ and ‘affixation-function’ are documented in the Elisp manual. If you use ‘consult--read’, you can specify an ‘:annotate’ keyword argument.

There is an exception to our recommendation: If you want to implement annotations for an existing package ‘hypothetic.el’, which does not have annotations and where annotations cannot be added, then the creation of a ‘marginalia-hypothetic.el’ package is a good idea, since Marginalia provides the facilities to enhance existing commands from the outside.

Commands that support minibuffer completion use a completion table of all the available candidates. Candidates are associated with a category such as ‘command’, ‘file’, ‘face’, or ‘variable’ depending on what the candidates are. Based on the category of the candidates, Marginalia selects an annotator to generate annotations for display for each candidate.

Unfortunately, not all commands (including Emacs’ builtin ones) specify the category of their candidates. To compensate for this shortcoming, Marginalia hooks into the Emacs completion framework and runs the classifiers listed in the variable ‘marginalia-classifiers’, which use the command’s prompt or other properties of the candidates to specify the completion category.

For example, the ‘marginalia-classify-by-prompt’ classifier checks the minibuffer prompt against regexps listed in the ‘marginalia-prompt-categories’ alist to determine a category. The following is already included but would be a way to assign the category ‘face’ to all candidates from commands with prompts that include the word "face".

emacs-lisp
(add-to-list 'marginalia-prompt-categories '("\\<face\\>" . face))

The ‘marginalia-classify-by-command-name’ classifier uses the alist ‘marginalia-command-categories’ to specify the completion category based on the command name. This is particularly useful if the prompt classifier yields a false positive.

Completion categories are also important for Embark, which associates actions based on the completion category and benefits from Marginalia’s classifiers.

Once the category of the candidates is known, Marginalia looks in the ‘marginalia-annotators’ to find the associated annotator to use. An annotator is a function that takes a completion candidate string as an argument and returns an annotation string to be displayed after the candidate in the minibuffer. More than one annotator can be assigned to each each category, displaying more, less or different information. Use the ‘marginalia-cycle’ command to cycle between the annotations of different annotators defined for the current category.

Here’s an example of a basic face annotator:

emacs-lisp
(defun my-face-annotator (cand)
  (when-let* ((sym (intern-soft cand)))
    (concat (propertize " " 'display '(space :align-to center))
            (propertize "The quick brown fox jumps over the lazy dog" 'face sym))))

After defining a new annotator, associate it with a category in the annotator registry as follows:

emacs-lisp
(add-to-list 'marginalia-annotators
             '(face my-face-annotator marginalia-annotate-face builtin none))

This makes the ‘my-face-annotator’ the first of four annotators for the face category. The others are the annotator provided by Marginalia (‘marginalia-annotate-face’), the ‘builtin’ annotator as defined by Emacs and the ‘none’ annotator, which disables the annotations. With this setting, after invoking ‘M-x describe-face RET’ you can cycle between all of these annotators using ‘marginalia-cycle’.