Emacs: mct version 1.0.0
Enhancements for the default minibuffer completion UI of Emacs. In essence, MCT is (i) a very thin layer of interactivity on top of the out-of-the-box completion experience, and (ii) glue code that combines built-in functionalities to make the default completion framework work like that of more featureful third-party options.
- Package name (GNU ELPA):
mct
- Official manual: https://protesilaos.com/emacs/mct
- Change log: https://protesilaos.com/emacs/mct-changelog
- Git repo on SourceHut: https://git.sr.ht/~protesilaos/mct
- Mirrors:
- GitHub: https://github.com/protesilaos/mct
- GitLab: https://gitlab.com/protesilaos/mct
- Mirrors:
- Mailing list: https://lists.sr.ht/~protesilaos/general-issues
- Video demo: https://protesilaos.com/codelog/2021-10-22-emacs-mct-demo/
- Backronym: Minibuffer Confines Transcended; Minibuffer and Completions in Tandem.
Below are the release notes.
Resumption of MCT development
In April 2022, I announced that I was discontinuing the development of
my mct
package. At the time, Emacs 29 was gaining new MCT-like
capabilities and I thought we would quickly reach a point where my
package would be superseded by built-in functionality. The article I
published at the time:
https://protesilaos.com/codelog/2022-04-14-emacs-discontinue-mct/.
About a year later and after receiving questions about MCT, I decided to restart its development. This was done in light of the realisation that the built-in Emacs functionality was still not as opinionated as MCT. There are good reasons for this state of affairs, due to the legacy of this important User Interface element and Emacs’ policy to not break stuff willy nilly. Still, the fact remains that MCT can fit in a very narrow niche for those who (i) like the built-in completions and (ii) appreciate a few extra niceties. What I wrote in March, 2023: https://protesilaos.com/codelog/2023-03-25-emacs-restart-mct-development/.
What does MCT offer that the built-in Emacs UI does not? In short:
-
MCT provides a facility for “live completions”, to automatically update the
*Completions*
buffer given certain conditions. A number of user options control the specifics. -
There are user options for a passlist and blocklist, which determine what should automatically display the
*Completions*
buffer and be live updated. The passlist and the blocklist can target individual commands, such asfind-file
, as well as completion categories likebuffer
. The manual includes a section with several known completion categories.
To be clear: MCT builds on top of the built-in functionality and
should not compete with it. Depending on my availability, I will try
to prepare patches for emacs.git to see whether at least some features
can be added directly to mnibuffer.el
or related.
MCT supports Emacs 29 or higher
MCT is highly opinionated about how the completions should work. This
applies to the presentation of the completion candidates as well as
the behaviour of commands that cycle between the minibuffer and the
*Completions*
, treating the two as a contiguous space. In previous
versions of Emacs, MCT could not work exactly as intended due to
limitations in the underlying framework. For example, the variable
completions-format
gained the one-column
value only in Emacs 28:
Emacs 27 supported grid views which are not intuitive as a vertical
list for up-down cycling between the candidates.
To make things easier to maintain, MCT only works with Emacs 29 or higher. The ~1 year hiatus has hopefully given users enough time to assess their options.
Deprecation of mct-region-mode
For a while, MCT supported in-buffer completion via a minor mode that would add all the needed functionality. This was always problematic due to underlying constrains and is thus no longer supported. MCT is designed to work exclusively with the minibuffer, where the behaviour is more reliable.
Nevertheless, users can still get an MCT-like experience with these settings, which affect the default UI (modify as you see fit):
;; Define the small wrapper functions
(defun my-mct-next-line-or-completion (n)
"Select next completion or move to next line N times.
Select the next completion if `completion-in-region-mode' is
active and the Completions window is on display."
(interactive "p")
(if (and completion-in-region-mode (mct--get-completion-window))
(minibuffer-next-completion n)
(next-line n)))
(defun my-mct-previous-line-or-completion (n)
"Select previous completion or move to previous line N times.
Select the previous completion if `completion-in-region-mode' is
active and the Completions window is on display."
(interactive "p")
(if (and completion-in-region-mode (mct--get-completion-window))
(minibuffer-previous-completion n)
(previous-line n)))
(defun my-mct-return-or-choose-completion (n)
"Choose current completion or create N newlines.
Choose the current completion if `completion-in-region-mode' is
active and the Completions window is on display."
(interactive "p")
(if (and completion-in-region-mode (mct--get-completion-window))
(minibuffer-choose-completion)
(newline n :interactive)))
;; Get the key bindings
(let ((map completion-in-region-mode-map))
(define-key map (kbd "C-n") #'my-mct-next-line-or-completion)
(define-key map (kbd "C-p") #'my-mct-previous-line-or-completion)
(define-key map (kbd "RET") #'my-mct-return-or-choose-completion))
;; Tweak the appearance
(setq completions-format 'one-column)
(setq completion-show-help nil)
(setq completion-auto-help t)
;; Optionally, tweak the appearance further
(setq completions-detailed t)
(setq completion-show-inline-help nil)
(setq completions-max-height 6)
(setq completions-highlight-face 'completions-highlight)
The mct-minibuffer-mode
is renamed to mct-mode
The mct-mode
was the original name, which was later given the
“minibuffer” specifier to disambiguate it from the aforementioned
mct-region-mode
. With the latter gone, this qualification is no
longer pertinent and the original name can be restored.
The completing-read-multiple
indicator has been removed
Previous versions of MCT would prepend a [CRM]
tag to the minibuffer
prompt of commands powered by completing-read-multiple
. While this
is a nice usability enhancement, it is not specific to MCT and thus
should not be part of mct.el
. Use this in your init file instead:
;; Add prompt indicator to `completing-read-multiple'. We display
;; [`completing-read-multiple': <separator>], e.g.,
;; [`completing-read-multiple': ,] if the separator is a comma. This
;; is adapted from the README of the `vertico' package by Daniel
;; Mendler. I made some small tweaks to propertize the segments of
;; the prompt.
(defun crm-indicator (args)
(cons (format "[`crm-separator': %s] %s"
(propertize
(replace-regexp-in-string
"\\`\\[.*?]\\*\\|\\[.*?]\\*\\'" ""
crm-separator)
'face 'error)
(car args))
(cdr args)))
(advice-add #'completing-read-multiple :filter-args #'crm-indicator)
No more IDO-like file navigation
Older versions of MCT had a command for file navigation that would
delete the whole directory component before point, effectively going
back up one directory. While the functionality can be useful, it is not
integral to the MCT experience and thus should not belong in mct.el
.
Add this to your own configuration file instead:
;; Adaptation of `icomplete-fido-backward-updir'.
(defun my-backward-updir ()
"Delete char before point or go up a directory."
(interactive nil mct-mode)
(cond
((and (eq (char-before) ?/)
(eq (mct--completion-category) 'file))
(when (string-equal (minibuffer-contents) "~/")
(delete-minibuffer-contents)
(insert (expand-file-name "~/"))
(goto-char (line-end-position)))
(save-excursion
(goto-char (1- (point)))
(when (search-backward "/" (minibuffer-prompt-end) t)
(delete-region (1+ (point)) (point-max)))))
(t (call-interactively 'backward-delete-char))))
(define-key minibuffer-local-filename-completion-map (kbd "DEL") #'my-backward-updir)
Lots of changes under the hood
I do not intend to refashion MCT. It works the way it was originally
intended to. What I did is to streamline the code for compatibility
with Emacs 29 and tweak the custom commands to preserve the desired
cyclic behaviour between the minibuffer and the *Completions*
.
Experiments such as integration with the avy
package or the ability
to type-to-complete in the *Completions*
buffer are abandoned.
Do not expect radical changes henceforth. I shall monitor and/or contribute to developments in core Emacs and am happy to forever archive MCT if/when the default completion UI gains the capabilities that, I think, make the user experience a little bit easier.