2024-02-26
How to make your GNU Emacs commands 'relevant' for M-X
Today I learned about the M-X command (well, key binding) (via), which "[queries the] user for a command relevant to the current mode, and then execute it". In other words it's like M-x but it restricts what commands it offers to relevant ones. What is 'relevant' here? To quote the docstring:
[...] This includes commands that have been marked as being specially designed for the current major mode (and enabled minor modes), as well as commands bound in the active local key maps.
If you're someone like me who has written some Lisp commands to customize your experience in a major mode like MH-E, you might wonder how you mark your personal Lisp commands as 'specially designed' for the relevant major mode.
In modern Emacs, the answer is that this is an extended part of
'(interactive ...)
', the normal Lisp form you use to mark your
Lisp functions as commands (things which will be offered in M-x and
can be run interactively). As mentioned in the Emacs Lisp manual
section Using interactive
,
'interactive
' takes additional arguments to label what modes your
command is 'specially designed' for; more discussion is in Specifying
Modes For Commands.
The basic usage is, say, '(interactive "P" mh-folder-mode)
'
If your commands already take arguments, life is simple and you can
just put the modes on the end. But not all commands do (especially
for quick little things you do for yourself). If you have just
'(interactive)
', the correct change is to make it '(interactive
nil mh-folder-mode)
'; a nil first argument is how you tell
interactive
that there is no argument.
(Don't make my initial mistake and assume that '(interactive ""
mh-folder-mode)
' will work. That produced a variety of undesirable
results.)
Is it useful to do this, assuming you have personal commands that are truly specific to a given mode (as I do for commands that operate on MH messages and the MH folder display)? My views so far are a decided maybe in my environment.
First, you don't need to do this if your commands have keybindings in your major mode, because M-X (execute-extended-command-for-buffer) will already offer any commands that have keybindings. Second, my assortment of packages already gives me quite a lot of selection power to narrow in on likely commands in plain M-x, provided that I've named them sensibly. The combination of vertico, marginalia, and orderless let me search for commands by substrings, easily see a number of my options, and also see part of their descriptions. So if I know I want something to do with MH forwarding I can type 'M-x mh forw' and get, among other things, my function for forwarding in 'literal plaintext' format.
With that said, adding the mode to '(interactive)' isn't much work and it does sort of add some documentation about your intentions that your future self may find useful. And if you want a more minimal minibuffer completion experience, it may be more useful to have a good way to winnow down the selection. If you use M-X frequently and you have commands you want to be able to select in it in applicable modes without having them bound to keys, you really have no choice.