109

How can I set a key binding that globally overrides and takes precedence over all other bindings for that key? I want to override all major/minor mode maps and make sure my binding is always in effect.

This of course doesn't work:

(global-set-key "\C-i" 'some-function)

It works in text-mode, but when I use lisp-mode, C-i is rebound to lisp-indent-line.

I can go through and override this binding in lisp-mode and in every other mode individually, but there must be an easier way. Every time I install a new mode for a new file type, I'd have to go back and check to make sure that all of my key bindings aren't being overridden by the new mode.

I want to do this because I want to emulate bindings I've already learned and ingrained from other editors.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Brian Carper
  • 71,150
  • 28
  • 166
  • 168
  • Cross-referencing with subsequent duplicate at https://emacs.stackexchange.com/questions/352/how-to-override-major-mode-bindings – phils Jul 13 '21 at 07:49

8 Answers8

159

I use a minor mode for all my "override" key bindings:

(defvar my-keys-minor-mode-map
  (let ((map (make-sparse-keymap)))
    (define-key map (kbd "C-i") 'some-function)
    map)
  "my-keys-minor-mode keymap.")

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

(my-keys-minor-mode 1)

This has the added benefit of being able to turn off all my modifications in one fell swoop (just disable the minor mode) in case someone else is driving the keyboard or if I need to see what a default key binding does.

Note that you may need to turn this off in the minibuffer:

(defun my-minibuffer-setup-hook ()
  (my-keys-minor-mode 0))

(add-hook 'minibuffer-setup-hook 'my-minibuffer-setup-hook)
Stefan
  • 27,908
  • 4
  • 53
  • 82
scottfrazer
  • 17,079
  • 4
  • 51
  • 49
  • 1
    This seems like a good idea. Is there any way to make sure your minor mode doesn't fight with other minor modes? – Brian Carper Mar 25 '09 at 21:51
  • 3
    Make sure your minor mode is first on the list minor-mode-map-alist. – Trey Jackson Mar 25 '09 at 21:54
  • 2
    Trey is right. Usually putting this near the end of your .emacs is enough. Also, most bindings you'd override would be ones that major modes are setting ... minor modes generally stay out of the way. – scottfrazer Mar 25 '09 at 22:04
  • I followed this approach, but then I realized that anything I bind to C-i also gets bound to the TAB key. Any suggestions? – Steve Nov 21 '09 at 22:38
  • @scottfrazer: why do I want to turn these bindings off in the minibuffer? Are there some special/important keybindings there that are likely to get clobbered, and if so, what are they? I ask because I just converted to this method, then entered the minibuffer and was shocked(!) to find that some of my global keybindings didn't apply. – Dave Abrahams Feb 24 '11 at 20:40
  • @dave-abrahams: It's a matter of taste and what your keybindings are. In this particular instance the key was C-i a.k.a. TAB which does many useful things in the minibuffer and you probably don't want it to, say, insert four spaces or whatever you have it do in a regular buffer. – scottfrazer Feb 25 '11 at 16:00
  • 3
    Brian Carper: Here's an enhancement to deal with subsequently-loaded minor modes: http://stackoverflow.com/questions/683425/globally-override-key-binding-in-emacs/5340797#5340797 – phils Mar 17 '11 at 14:59
  • This is nice. Thanks scottfrazer :) – Sujoy Sep 04 '11 at 13:03
  • Is there an easy way to override the keybinds in this minor-map ? That would be neat: "always use the keybinds in the map, unless I explicitly override them for a specific mode-map". – Norswap Jul 20 '12 at 01:07
  • Scott, don't you need to hook into `minibuffer-exit-hook` as well to turn your minor mode back on after leaving the minibuffer? It seems to be the case now that I've tried it on my system. Without it, the mode gets deactivated on first entry into the minibuffer and it stays that way. – Endre Both Oct 04 '13 at 20:58
  • @EndreBoth, not that I know of. The minor-mode is per-buffer so shouldn't be switched off in other buffers. – scottfrazer Oct 10 '13 at 13:56
  • @Steve Tab is bound to C-i by default in one of the key translation keymaps, which is a lower level kind of binding within emacs. You can `define-key` to that keymap as you would any other. – ealfonso Nov 08 '13 at 06:42
  • Is it implied somewhere in this snippet which map to use? I tried it and it works, I just don't understand why we're not using the `KEYMAP` parameter to `define-minor-mode` – trey-jones Aug 08 '19 at 17:30
  • Would it be wise to write all my keybindings into the function? @scottfrazer – alper Jun 30 '20 at 11:01
33

As an addition to scottfrazer's answer, I've written the following so that my keybindings retain precedence, even if subsequently-loaded libraries bring in new keymaps of their own.

Because keymaps can be generated at compile time, load seemed like the best place to do this.

(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) 'my-keys-minor-mode)
    (let ((mykeys (assq 'my-keys-minor-mode minor-mode-map-alist)))
      (assq-delete-all 'my-keys-minor-mode minor-mode-map-alist)
      (add-to-list 'minor-mode-map-alist mykeys))))
Community
  • 1
  • 1
phils
  • 71,335
  • 11
  • 153
  • 198
  • I pasted your script but it didn't make any affect :( – alper Dec 30 '19 at 10:31
  • @alper I suggest that you post a question with all of the relevant details, including the code you're actually using, and a specific example/recipe to reproduce the problem. – phils Dec 30 '19 at 11:14
  • 1
    @alper I am using the code here (https://github.com/dabrahams/dotfiles/blob/f683c5338191dd6e8af8cf44cc446a33b4569c2a/emacs/.emacs.d.default/init/dwa-global-keybindings.el) and it seems to work perfectly, keeping my keymap at the highest priority. – Dave Abrahams Mar 12 '23 at 05:56
29

Install use-package, eval and you're done:

(require 'bind-key)
(bind-key* "C-i" 'some-function)
Mirzhan Irkegulov
  • 17,660
  • 12
  • 105
  • 166
15

I found this question while searching for "emacs undefine org mode keybindings", because I wanted to unbind the existing C-c C-b behavior to allow my global map to bury-buffer to work in an org buffer.

This ended up being the simplest solution for me:

(add-hook 'org-mode-hook
      (lambda ()
        (local-unset-key (kbd "C-c C-b"))))
tshepang
  • 12,111
  • 21
  • 91
  • 136
Jay Doane
  • 306
  • 3
  • 4
  • 1
    This is mode-specific and doesn't address the bigger picture even though it does work for your single use case. – RichieHH Feb 14 '20 at 08:03
12

Although scottfrazer's answer is exactly what you asked for, I will mention for posterity another solution.

From The Emacs Manual:

"Don't define C-c letter as a key in Lisp programs. Sequences consisting of C-c and a letter (either upper or lower case) are reserved for users; they are the only sequences reserved for users, so do not block them."

If you bind your personal global bindings to C-c plus a letter, then you "should" be safe. However, this is merely a convention, and any mode is still able to override your bindings.

Kirkland
  • 164
  • 1
  • 5
  • 3
    I didn't expect org-mode, of all modes, to break this rule. `C-c C-h' tells me that C-c a, b, c, and l are bound to org-agenda, org-iswitchb, org-capture, and org-store-link, respectively. – Nate Parsons Aug 12 '12 at 22:35
  • 8
    Afaik, binding these is the first step org-mode suggests in order to use it, but the user has to define them himself (i.e. it's not done by default), and may choose any other while doing so. (also, it's because these bindings are supposed to be global, not bound to the org major mode) – Nikana Reklawyks Oct 09 '12 at 03:14
  • `C-c b` is [no longer suggested in the manual](https://orgmode.org/manual/Activation.html#Activation). – NickD Oct 21 '20 at 07:17
3

If you want to "always use the keybinds in the map, unless I explicitly override them for a specific mode-map", and assuming you are using scottfrazier's approach, you want:

(defun locally-override (key cmd)
  (unless (local-variable-p 'my-keys-minor-mode-map)
    (set (make-variable-buffer-local 'my-keys-minor-mode-map)
         (make-sparse-keymap))
    (set-keymap-parent my-keys-minor-mode-map 
                       (default-value 'my-keys-minor-mode-map)))
  (define-key my-keys-minor-mode-map key cmd))

So

(locally-override "\C-i" nil)

should remove the "\C-i" binding from the minor mode in the current buffer only. Warning: this is completely untested, but seems like the right approach. The point of setting the parent rather than just coping the global value of my-keys-minor-mode-map is so any later changes to the global value are automatically reflected in the local value.

Community
  • 1
  • 1
gbrunick
  • 61
  • 2
2

I don't think you can. That is roughly equivalent to saying that you want to define a global variable that cannot be hidden by local variable declarations in functions. Scope just doesn't work that way.

However, there might be a way to write an elisp function to go through the mode list and reassign it in every single one for you.

T.E.D.
  • 44,016
  • 10
  • 73
  • 134
  • This idea of scoping is technically correct, but `overriding-local-map` is specifically designed to override all other maps. However it's dangerous to use it. – event_jr Apr 01 '14 at 13:16
2

Unless you really want to do this yourself, you should check around and see if anyone else already has done it.

There is a package for Emacs which gives your windows-like keybindings. You should be able to find it through google.

JesperE
  • 63,317
  • 21
  • 138
  • 197