Global key bindings in Emacs
Global key bindings in emacs. What's hard about that, right? Just something simple like
(global-set-key "\C-m" 'newline-and-indent)and you're all set.
Well, no. global-set-key
gives you a nice key binding
that works ... until the next time you load a mode that wants
to redefine that key binding out from under you.
For many years I've had a huge collection of mode hooks that run when specific modes load. For instance, python-mode defines \C-c\C-r, my binding that normally runs revert-buffer, to do something called run-python. I never need to run python inside emacs -- I do that in a shell window. But I fairly frequently want to revert a python file back to the last version I saved. So I had a hook that ran whenever python-mode loaded to override that key binding and set it back to what I'd already set it to:
(defun reset-revert-buffer () (define-key python-mode-map "\C-c\C-r" 'revert-buffer) ) (setq python-mode-hook 'reset-revert-buffer)
That worked fine -- but you have to do it for every mode that overrides key bindings and every binding that gets overridden. It's a constant chase, where you keep needing to stop editing whatever you wanted to edit and go add yet another mode-hook to .emacs after chasing down which mode is causing the problem. There must be a better solution.
A web search quickly led me to the StackOverflow discussion Globally override key bindings. I tried the techniques there; but they didn't work.
It took a lot of help from the kind folks on #emacs, but after an hour
or so they finally found the key: emulation-mode-map-alists
.
It's only
barely
documented -- the key there is "The “active” keymaps in each alist
are used before minor-mode-map-alist and minor-mode-overriding-map-alist" --
and there seem to be no examples anywhere on the web for how to use it.
It's a list of alists mapping names to keymaps. Oh, clears it right up! Right?
Okay, here's what it means. First you define a new keymap and add your bindings to it:
(defvar global-keys-minor-mode-map (make-sparse-keymap) "global-keys-minor-mode keymap.") (define-key global-keys-minor-mode-map "\C-c\C-r" 'revert-buffer) (define-key global-keys-minor-mode-map (kbd "C-;") 'insert-date)
Now define a minor mode that will use that keymap. You'll use that minor mode for basically everything.
(define-minor-mode global-keys-minor-mode "A minor mode so that global key settings override annoying major modes." t "global-keys" 'global-keys-minor-mode-map) (global-keys-minor-mode 1)
Now build an alist consisting of a list containing a single dotted pair: the name of the minor mode and the keymap.
;; A keymap that's supposed to be consulted before the first ;; minor-mode-map-alist. (defconst global-minor-mode-alist (list (cons 'global-keys-minor-mode global-keys-minor-mode-map)))
Finally, set emulation-mode-map-alists to a list containing only the global-minor-mode-alist.
(setf emulation-mode-map-alists '(global-minor-mode-alist))
There's one final step. Even though you want these bindings to be global and work everywhere, there is one place where you might not want them: the minibuffer. To be honest, I'm not sure if this part is necessary, but it sounds like a good idea so I've kept it.
(defun my-minibuffer-setup-hook () (global-keys-minor-mode 0)) (add-hook 'minibuffer-setup-hook 'my-minibuffer-setup-hook)
Whew! It's a lot of work, but it'll let me clean up my .emacs file and save me from endlessly adding new mode-hooks.
[ 16:46 Sep 14, 2014 More linux/editors | permalink to this entry | ]