Mastering Key Bindings in Emacs

Altering the key bindings in Emacs should not, on the face of it, be a difficult task. But there’s a reason why the Emacs manual has dedicated 30-odd pages to describing, in great detail, all the subtleties and nuances of how to bind keys.

To save you the time of reading all of that, I’ve written a guide that covers what you need to know to bind keys to even complex commands, and a set of templates you can use in your own code.

This guide will assume that you have little or no knowledge of elisp

What makes up a key bind?

Emacs is “self-hosting” and, depending on who you ask, close to achieving sentience. Almost all of Emacs is written in Emacs-Lisp (weighing in at roughly 1.2 million lines of code!), and most of the complex elisp functions you use are in turn built with simpler building blocks and so on, right down to the core C source code layer.

Emacs’s key engine is very similar, because Emacs lets you control almost every facet of self-insertion and other key binds. For instance, when you press “f” it runs self-insert-command, a special command that repeats the last typed key N times. So if you type C-u 10 f you will see ffffffffff printed on your screen. As you can see, every keypress – even elemental ones like typing a character on the screen – has an associated key binding that you can modify or even remove.

Keymaps

A keymap is an internal data structure used by Emacs to store keys and their associated actions. Keymaps are rarely modified directly, but through a set of commands that manipulate the data structure for you. Most Emacs users will never interact with keymaps aside from indirectly assigning keys to them.

Every buffer and most major and minor modes have a keymap, and that keymap defines what the keys do when key sequences are sent to that buffer. Keys can be divided into three categories: undefined, prefix key, or complete key. Undefined is self-explanatory: it does no operation when it is invoked. Prefix keys are keys like C-x and C-c; they are parts of a complete key, and each constituent part of a prefix key is made up of its own keymap. A complete key is a command that, when input, executes its associated command.

It is possible for Emacs to enumerate all the active minor and major mode key bindings in a buffer by typing C-h m. This command is very useful if you want to learn more about what a major or minor mode can do. Likewise, you can type out parts of a complete key (say M-s) and then type C-h to get a list of all keys that belong to that prefix.

Key Bind Commands

There are several ways you can define (or undefine) keys, as the table below shows, but in reality there are dozens of ways you can hack the keymaps.

(define-key KEYMAP KEY DEF)

Defines a key against a keyboard map. Use this if you want to change a keymap that isn’t the current buffer map.

(local-set-key KEY COMMAND)

Binds a key to the local keymap used by the active buffer, unlike define-key which takes an explicit keymap to bind a key against.

(global-set-key KEY COMMAND)

Binds a key to the global keymap, making it available in all buffers (with a caveat – see below.)

(global-unset-key KEY)

Removes KEY from the global keymap

(local-unset-key KEY)

Removes KEY from the active, local keymap.

Representing Keys in Code

In order to actually bind a key you must first tell Emacs what key you intend to use. Unfortunately there’s more than one way of representing keys in Emacs: as a string, or as a vector. We won’t concern ourselves with either, as we will use a handy macro built in to Emacs called kbd.

The kbd macro translates a human-readable key into a format Emacs can understand.

One important point to note is that you must surround function and navigation keys with < and >. Those keys include F-keys, arrow keys and home row keys, like so: <home>, <f8> and <down>. But if you want represent the key C-c p then write (kbd "C-c p").

Remapping Commands

You can tell Emacs that you want to replace all keys pointing to a certain command with one of your own choosing by using the remap event; this should be done instead of passing a key to the key bind function you are using. This is arguably the best way of replacing existing commands with your own as Emacs will automagically handle the key reassignment in the background.

Example:

(define-key (current-global-map) [remap kill-line] 'my-homemade-kill-line)

Here I globally remap all key binds that point to kill-line to my-homemade-kill-line.

For some more hands-on examples read my article Fixing the mark commands in transient mark mode.

Reserved Keys

You can pick any keyboard combination you desire – even if that key bind is already taken, so be careful – but Emacs has set aside certain keys for use by users. Generally, all keys prefixed with C-c ? (where ? is a single character) are reserved for you, and you alone. In practice most third-party packages don’t give a hoot and will gladly stuff their own key binds in there.

The other set of reserved keys are the F-keys from F5 and onwards. The other two prefix keys reserved to you are hyper and super. They are remnants from ancient keyboards used in the 80s, but live on today in Emacs. Most PC-compatible keyboards won’t have a super or hyper key so some people rebind the Windows key and the Application Context key to be hyper and super instead.

Anyway, if you want to use hyper then use the prefix key H- (e.g., H-q) and if you want super use the prefix key s-. Take note of the lower case ‘s’!

In Windows you can add this to to your .emacs to enable hyper and super:

(setq w32-apps-modifier 'hyper)
(setq w32-lwindow-modifier 'super)
(setq w32-rwindow-modifier 'hyper)

In X you’ll have to play around with xmodmap or your own tool of choice.

Keymap Lookup Order

Emacs will look for keys in a certain order, and that order I have described below. Keep in mind that only active keymaps are used, and that the order is top-to-bottom; the first “match” is used, subject to criteria that we don’t care about.

  1. overriding-terminal-local-map for terminal-specific key binds.
  2. overriding-local-map for keys that should override all other local keymaps. Be VERY careful if you use this!
  3. Keymap char property at point for keymaps that are local to the character point is at. This is used for stuff like fields in yasnippet and the customize dialog.
  4. emulation-mode-map-alists for advanced multi-mode keymap management
  5. minor-mode-overriding-map-alist for overriding the keymaps used by minor modes in major modes.
  6. minor-mode-map-alist is exactly like the overriding version above, but the preferred means of specifying the keymaps for minor modes.
  7. Keymap text property at point is like the one above for char properties but is for text properties only.
  8. current-local-map for keymaps defined in the buffers’ current local map
  9. current-global-map is the last place Emacs will look for key binds and it is for the global ones.

All you care about is that minor mode keys come before local keys, and they in turn are checked before global keys.

Global vs Local

A global key is functionally identical to that of a local one, except it is declared in a “global” keymap, governed by the current-global-map function (but usually it points to the default, the global-map variable.) Therefore, it is possible for you to define a global key simply by passing the current-global-map function to define-key. The other – often better – alternative is to use the designated function, global-set-key.

In a similar vein, to bind a local key you can use the “designated” function local-set-key or the more general define-key. Like the global map, there exists an equivalent function current-local-map that returns the keymap local to the buffer. It’s important that you know that although the major mode in a buffer will define the bulk of the key binds used in a buffer, the minor modes often add, remove or change the key binds as they take precedence over the local keymap.

Defining your Command

If you thought the mechanics of keymaps and keys were difficult, think again! Deciding on what you want to bind to those keys is even harder – especially so if you want the command to do very specific things, like switching to a specific buffer name. But I’ve got a few tricks up my sleeve to help you cut out most of the elisp writing by letting Emacs do all the heavy lifting.

Invoking a command

Before I talk about the how and why of Emacs commands it is important that I mention how Emacs handles interactive functions – commonly known as commands. An Emacs command is one that has the (interactive ...) statement at the top of a function body; this magic statement tells Emacs the command is interactive and that it is intended for consumption by users. That is also what determines if you can invoke a command through the M-x prompt.

When you bind a function to a key it is important that you keep the above in mind, as you cannot invoke a non-interactive function through a key binding – it must be a command. Also, for a key definition to function it must invoke a command with no parameters. If it is your intent – as it so often is – to call a function with parameters you must wrap it in a little helper function, like a lambda expression or even a standard defun. I have prepared templates (see below) that show you how to do this.

Describing the command

There are two ways of describing a command in elisp: the manual way, and the smart way. Let’s start out with the manual way.

The manual way is to use C-h k KEY to describe what KEY is bound to; the other way is to describe a function, FUNCTION, with C-h f FUNCTION. Because Emacs is a self-documenting editor all functions, variables, keys, etc. loaded in Emacs can be accessed by way of the describe-xxx commands (type C-h C-h to see them all!) and that is what most Emacs hackers use.

But there’s another way… the smarter way. It has one teensy-weensy little downside: it’ll only work for interactive functions (the ones we call commands, remember?), and then only complex commands – that is, a command that needs user input from the minibuffer.

The smarter way has a name, repeat-complex-command, and it is conveniently bound to the key C-x ESC ESC. When you type it, Emacs will ask you to redo the last typed command, but it will do so showing you the elisp expression to execute; so you can not only change it then-and-there and re-run it, but you can copy it and use it in your own code!.

Here’s a practical example: C-M-% foo RET bar RET – which does a query-replace-regexp replacing foo with bar. Now, if you type C-x ESC ESC you should see something similar to this (reformatted for clarity):

(query-replace-regexp "foo" "bar" nil
   (if (and transient-mark-mode mark-active)
       (region-beginning))
   (if (and transient-mark-mode mark-active)
       (region-end)))

Observe how Emacs has kindly filled in all the function arguments to query-replace-regexp, including the optional parameters. If you were to run that command again (by say pasting it into the prompt in M-:) you will be asked to interactively search and replace with the search term foo and its replacement bar already filled in!

Here’s another one, this time I type C-x b *scratch* to switch to the *scratch* buffer:

(switch-to-buffer "*scratch*")

And there you have it. Obviously the commands seen here are the interactive commands. Stuff like replacing strings in a buffer is normally done with specialist elisp functions that don’t require user input, but that may not be a big deal if you are writing your own quick-and-dirty key binds.

Putting it all together

Yay. You’ve made it this far. I just need to explain a few more important concepts that confuse a lot of newbies: mode hooks and mode-specific keymaps, and then it’s on to the templates.

Most major and minor modes will usually set their keys once, when the module is first loaded; that’s good news for us, as we can use define-key to add key definitions straight into their mode map, the map that holds keys relevant to that mode. Some modes are very advanced, and have several maps – isearch is a good example – and for those you will have to mess around with the source code (or possibly the info manual) to find out how to add keys.

It’s the modes that force you to use a mode hook that’s the problem: for that you must set your keys when the mode is launched (using its mode hook), and that takes a little bit more work.

It is a convention in Emacs that all major mode functions (the ones that activate the mode) end in -mode (e.g., python-mode), and it is also required that its mode map is named xxxx-mode-map, and its major mode hook xxxx-mode-hook.

Keymaps

Modifying a keymap is dirt simple, you use define-key as I’ve mentioned before. What’s not so easy is determining what keymap to modify in the first place.

Listing all the Mode Maps

If you type this Emacs will give you an apropos buffer with all the known mode maps that follow the major mode naming scheme:

C-u M-x apropos-variable RET -mode-map$ RET

Amusingly, you can use the repeat-complex-command trick to get the expression for use in a key bind or custom function…

Quick Keymap Example

This assumes you are using the built-in python mode that comes with GNU Emacs

Let’s say I want to extend python-mode by adding a key, I’ll use F12, that switches to the python process buffer. Currently, that’s bound to C-c C-z.

(define-key python-mode-map (kbd "<f12>") 'python-switch-to-python)

I had to use the manual way (C-h k C-c C-z) to determine the name of the command because the smart way wouldn’t work: the command is not complex, as it does not require user input from the minibuffer.

Hooks

A mode hook has zero-or-more functions that are called when its mode is activated, like say when you open a file that uses that mode or when you change the major mode in a buffer.

You can add a mode hook using the special function add-hook that takes the name of a hook (say python-mode-hook) and the name of a function to call.

Listing all the Mode Hooks

If you are unsure of the exact name of the mode hook, you can use this handy trick to list the ones Emacs can see:

C-u M-x apropos-variable RET -mode-hook$ RET.

That will show all mode hooks known to Emacs, including their docstring description. If you don’t see your mode it may be because Emacs hasn’t loaded it outright or it lacks the autoload keyword.

Quick Hook Example

Let’s add a key (C-c q to run M-x shell) local to python-mode using a hook. For that to work we will need our special hook function, I’ve named it mp-add-python-keys, and in it we need local-set-key, the function that adds a key to the active buffer’s local map. Observe that the command will be called within the context of the buffer that invoked the major mode.

Next is the command that tells Emacs that we want to add a mode hook to Python.

(defun mp-add-python-keys ()
  (local-set-key (kbd "C-c q") 'shell))

(add-hook 'python-mode-hook 'mp-add-python-keys)

Templates

Here’s a bunch of templates for various use-cases that you can cut’n’paste and use in your own code. I recommend naming things sensibly, and giving them a docstring (in-code documentation that explains what the code does) as well. The best way to avoid accidentally overriding another function with the same name, I would suggest you use a moniker or prefix (I use mp-.)

I suggest you read my article, Evaluating Elisp in Emacs, to learn how to evaluate and test the code you write!

Function Template

Purpose

Required if you want to invoke non-interactive functions or functions/commands with parameters. Use this functions’ name in the command definition argument.

Definition

(defun my-function-name-here ()
  (interactive)
  ;;; Place your code below this line, but inside the bracket.
  )

Example

Displays the message “Hello, World” in the echo area when invoked.

(defun mp-display-message ()
  (interactive)
  ;;; Place your code below this line, but inside the bracket.
  (message "Hello, World")
  )

Basic Global Key Bind

Purpose

Creates a global key bind available to all buffers.

Definition

(global-set-key (kbd "key-bind-here") 'interactive-command-here)

Example

Binds F1 to M-x shell

(global-set-key (kbd "<f1>") 'shell)

Add key definition to a keymap

Purpose

Adds a key and its associated command to a local keymap. Useful for extending major modes with your own custom key binds.

Definition

(define-key KEYMAP (kbd "key-bind-here") 'interactive-command-here)

Example

Binds C-c p to python-switch-to-python

(define-key python-mode-map (kbd "C-c p") 'python-switch-to-python)

Complex Command Key Bind

Purpose

Creates a global key bind that invokes multiple commands in a row. Use this to create compound keys or invoke commands that take parameters. Uses code from the Function Template.

Definition

(defun name-of-interactive-command-here ()
  (interactive)
  ;;; Insert your compound commands below

  )
(global-set-key (kbd "key-bind-here") 'name-of-interactive-command-here)

Example

Switches to the *scratch* buffer and inserts “Hello, World” where point is, and switches back to where it came from.

(defun switch-to-scratch-and-insert-text ()
  (interactive)
  (save-excursion
    (set-buffer "*scratch*")
    (insert "Hello, World")))
(global-set-key (kbd "C-c i") 'switch-to-scratch-and-insert-text)

Binding keys with a mode hook

Purpose

Use this mode hook template to bind keys that won’t work with a standard define-key template or that require local binding for other reasons. The advantage of a mode hook is that it also gives you the opportunity to set mode-specific settings like indentation, etc.

Definition

(defun my-hook-function ()
  ;; add your code here. it will be called every
  ;; time the major mode is run.

  )
(add-hook 'my-mode-hook 'my-hook-function)

Example

Make the return key automatically indent on a newline in emacs-lisp-mode, and enable eldoc mode as well.

(defun enable-my-elisp-settings ()
  (turn-on-eldoc-mode)
  (local-set-key (kbd "C-m") 'newline-and-indent))
(add-hook 'emacs-lisp-mode-hook 'enable-my-elisp-settings)

Remapping a function

Purpose

This template will remap all keys that point to a specific function; say you want to rebind kill-line which is bound to C-k but you want your code to only override the key binds that kill-line is actually bound to. Use this template to replace existing commands with those of your own, without worrying about explicitly rebinding each key.

Definition

Note: to replace a global key, you must use global-map or call current-global-map.

(define-key keymap [remap original-function] 'my-own-function)

Example

Example here is taken from my article on fixing the mark commands in transient mark mode. I remap the keys that point to exchange-point-and-mark to my own function exchange-point-and-mark-no-activate.

(defun exchange-point-and-mark-no-activate ()
  "Identical to \\[exchange-point-and-mark] but will not activate the region."
  (interactive)
  (exchange-point-and-mark)
  (deactivate-mark nil))
(define-key global-map [remap exchange-point-and-mark] 'exchange-point-and-mark-no-activate)

Custom Prefixes

Purpose

Creating a prefix is easy nowadays as you don’t have to explicitly create your own prefix keymaps, provided you use local-set-key or global-set-key. Use custom prefixes to group or categorize your commands.

Definition

(global-set-key (kbd "subkey_1 ... endkey_1") 'my-command-1)
(global-set-key (kbd "subkey_1 ... endkey_2") 'my-command-2)

Example

Global keys that will insert either the time of the day, or the current date. Type C-c i d to insert the date; and C-c i t to insert the time. Type C-c i C-h to list all bound keys under the C-c i prefix.

(defun mp-insert-date ()
  (interactive)
  (insert (format-time-string "%x")))

(defun mp-insert-time ()
  (interactive)
  (insert (format-time-string "%X")))

(global-set-key (kbd "C-c i d") 'mp-insert-date)
(global-set-key (kbd "C-c i t") 'mp-insert-time)

Conclusion

I have covered almost every facet of key binding that most Emacs users would care to know about. I know there’s a lot of tutorials out there that essentially capture only what I have in the templates section, but for your edification I decided I wanted to cover everything (or near as well anyway) that make up a key definition.

That I can dedicate so much to a topic that on the outset appears straightforward highlights the flexibility – and complexity – of Emacs. I hope the guide has taught you how to do one of the most frequently-asked things in Emacs.