Customizing the Emacs Tools Menu
24 Oct 2023 Charles Choi
Truth be told, I’ve always found the default Emacs Tools menu to be weird. But that’s really a matter of taste and rather than litigate the default configuration of the Tools menu, let’s talk about how you can customize the Tools menu to your liking.
Some Background on Emacs Menus
Emacs menus are defined as keymaps, a data structure that holds information like the label, command, and bindings (key or mouse) per menu entry stored in a keymap. Menu keymaps can be edited, which enables one to arbitrarily customize them to their desired taste.
Identifying Existing Menu Items
Use the command describe-key
(C-h k
) to identify an existing menu item selected by the mouse. An Emacs window describing the command will be displayed showing the hierarchical keymap path to the menu item, with each keymap identified between angled brackets (<>). We’ll use this keymap path to identify menu items to remove or add.
For a more comprehensive view of a keymap, use the describe-keymap
function. This enables you to see all the keymap paths in one step. To see keymap paths (or bindings) of the Tools menu, issue the following command:
1 |
|
Removing Menu Items
To demonstrate removing a menu item, let’s remove the default item Tools ‣ Project Support (EDE). Using the describe-key
function, we see that its keymap path is <menu-bar> <tools> <ede>
. Use the function define-key
to remove (or hide) the menu entry. The function signature for define-key
is shown below:
1 |
|
To remove the EDE menu item use the following argument values:
Argument | Value | Notes |
---|---|---|
KEYMAP | global-map | Default global keymap. |
KEY | [menu-bar tools ede] | Vector of hierarchical keymap path to menu item. |
DEF | nil | In versions older than 29.1, a nil value will hide the item. |
REMOVE | t | Only available in 29.1+. If this value is non-nil, then the item will be removed. |
Note that the REMOVE
argument is new in Emacs 29.1. Beforehand you could only hide a menu entry when DEF
is set to nil
. Equipped with the above info, we can remove Tools ‣ Project Support (EDE) using the following Elisp:
1 |
|
Adding Menu Items
While Emacs provides multiple approaches to adding a menu item, easy-menu-add-item
is relatively straightforward as it allows one to pass either a vector type or another menu (defined by easy-menu-define
) as the ITEM
argument.
1 |
|
To see the above command in action, let’s implement a menu item for magit-status
and place it above the existing menu item whose label is “Version Control”. We will also make it conditionally visible if the default-directory
of the current Emacs window is under version control.
1 2 3 4 5 6 |
|
If ITEM
is a vector type, then different keywords are supported as described here. Use describe-function
to get documentation on easy-menu-add-item
.
Defining Separators
The new 29.1 keymap-set-after
function lets you add a menu separator with the benefit of defining keywords such as :enable
or :visible
to provide control over when the separator is displayed. An example of this is described below.
1 2 3 4 |
|
My Tools Menu
With the above you are now equipped to configure your Tools (or really, any menu) to your taste. As of this writing, my customized Tools menu looks like this on Emacs 29.1:
The Elisp to configure the above menu is found at this source.
Closing Thoughts
Among the many benefits that menus provide is to lower the cognitive load of memorizing commands. But those benefits are only seen if the menu commands are useful to the user. Customizing menus provides a path for the user to maximize their utility. Done judiciously, customizing Emacs menus can significantly enhance the overall experience of using Emacs.
Addendum 2023-11-02
Dan Drake made two excellent notes on this post which I've amended accordingly. First is that the predicate using the function vc-responsible-backend
for the key :visible
will return an error object if applied to a directory or file that is not under version control. The :visible
key in an Emacs menu item would rather see a nil
value than the aforementioned error object. To support this, you must set the NO_ERROR
argument to t
as follows:
1 |
|
The second note is to use the function describe-keymap to show all of a keymap's bindings in one step.