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
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:
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:
To remove the EDE menu item use the following argument values:
|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:
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
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
ITEM is a vector type, then different keywords are supported as described here. Use
describe-function to get documentation on
The new 29.1
keymap-set-after function lets you add a menu separator with the benefit of defining keywords such as
: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.
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.
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:
The second note is to use the function describe-keymap to show all of a keymap's bindings in one step.