Rebinding Keys, or, The Horror of Alt+TAB in Emacs

Table of Contents

I use exwm so M-TAB is available to me without being hijacked by the OS, but rebinding this failed in surprising places. I want it globally to be set to iflipb-next-buffer (giving familiar alt+tab functionality to exwm), but if any of the buffers I’m travelling past happen to inherit magit or gnus, my tab-sequence gets broken because they have it bound to their own thing and I can’t seem to rebind it. It turns out that there are at least five ways to bind M-TAB and, it seems, you have to match the right ones if you want to over-write something. The two culprits that made my life difficult were magit (also because of modes that inherit from magit) and gnus. Gnus is a special case because it dates back to almost the dawn of emacs, meaning that the ways of binding keys try to compensate for generations that pre-date modern keyboards and operating sytems.

Now, as is always a good idea, I’ve got my custom global keybindings in my own map so I can easily turn them off to get back to base-truth. These are the five ways I had to bind in order to guarantee my M-TAB over-wrote all the bindings in child modes.

Custom-Minor Mode that Captures M-TAB

(defvar tsa-keys-map
  (let ((map (make-sparse-keymap)))
    (define-key map (kbd "M-TAB") 'iflipb-next-buffer) ;; this works most of the time
    (define-key map [(meta tab)] 'iflipb-next-buffer)  ;; gnus
    (define-key map "\M-\C-i" 'iflipb-next-buffer) ;;  gnus
    (define-key map (kbd "M-C-i") 'iflipb-next-buffer) ;; gnus
    (define-key map [M-tab] 'iflipb-next-buffer) ;; from magit
    map)
  "tsa-keys-minor-mode keymap.")

(define-minor-mode tsa-keys
  "A minor mode so that my key settings override annoying major modes."
  :init-value t
  :lighter " ")

(global-set-key (kbd "<C-f3>") 'tsa-keys)
(tsa-keys 1)

Ensuring over-writes work

And now giving priority to my minor-mode so it is always the final minor-mode:

(add-hook 'after-load-functions 'my-keys-have-priority)

(defun my-keys-have-priority (_file)
  "Try to ensure that my keybindings retain priority over other minor modes.

Called via the `after-load-functions' special hook."
  (unless (eq (caar minor-mode-map-alist) 'tsa-keys)
    (let ((mykeys (assq 'tsa-keys minor-mode-map-alist)))
      (assq-delete-all 'tsa-keys minor-mode-map-alist)
      (add-to-list 'minor-mode-map-alist mykeys))))

Resources

Comments?

If anyone can shed some light on these different ways of binding M-TAB they would be very welcome, and I’ll update this post accordingly.

Tory Anderson avatar
Tory Anderson
Full-time Web App Engineer, Digital Humanist, Researcher, Computer Psychologist