38

Sometimes my global keybindings are overridden by a major mode. An easy example is the following setting in my init file

(global-set-key (kbd "C-j") 'newline-and-indent)

But annoyingly this keybinding is hidden by the "Lisp Interaction" major mode which is the default mode of the scratch buffer.

When I find myself in a situation where a major mode (or minor mode) is hiding my global keybinding, how can I get it back?

Note: My question is not "How can I bind C-j to newline-and-indent in "Lisp Interaction" mode?" I am interested in a much more general answer about how to deal with keymaps that clash or user keybindings that get hidden by some major/minor mode.

39

There is a "shortcut" approach too for the same solution if you don't want to define your own minor mode (that I talk about in my first answer).

You can install the use-package package available from Melpa and make use of bind-key* or bind-keys* macro that's part of the bind-key package that ships with use-package.

From the documentation of bind-key.el:

;; If you want the keybinding to override all minor modes that may also bind
;; the same key, use the `bind-key*' form:
;;
;;   (bind-key* "<C-return>" 'other-window)

;; To bind multiple keys in a `bind-key*' way (to be sure that your bindings
;; will not be overridden by other modes), you may use `bind-keys*' macro:
;;
;;    (bind-keys*
;;     ("C-o" . other-window)
;;     ("C-M-n" . forward-page)
;;     ("C-M-p" . backward-page))
  • In case anyone is curious, note that this just uses a minor mode behind the scenes. It does take a different approach to conflicts with other minor modes, by using emulation-mode-map-alists to enforce precedence). – phils Oct 8 '14 at 21:04
  • I have marked this as the answer because of its simplicity. It is essentially doing the same thing as this answer, but it is conveniently doing most of the work for you behind the scenes. – nispio Oct 12 '14 at 13:46
  • @nispio You're correct. From my experience the only difference is the method that's used to make the minor mode bindings override other modes globally. But the advantage of the other approach (of having your own minor mode) is that you can turn it off momentarily to try out the original emacs bindings if you ever need to ( I have needed to do that a few times). – Kaushal Modi Oct 12 '14 at 15:02
  • 1
    Add (bind-key* "C-M-&" 'override-global-mode), to your init, and you can usually get the bindings out of your way quick if you need to. Since override-global-mode is not a "global" minor mode, you will still need to deactivate it on a per-buffer basis. So if you find yourself frequently deactivating the global override keys then this solution is not convenient. – nispio Oct 12 '14 at 15:56
25

You can define your own minor mode and its key map and have that override all other modes (minor + major). That's exactly why I chose to write my own minor mode.

Steps to have your key bindings override all bindings:

  • Defining your own minor mode and key map as shown below.
  • Activate your minor mode globally
  • (define-key my-mode-map (kbd "C-j") #'newline-and-indent)

Similarly your other key bindings set in your minor mode will override those in other modes.

I highly recommend reading the blog post by Christopher Wellons on how to write a minor mode. That blog plus the annoyance of having to set multiple key bindings to nil in multiple major and minor modes inspired me to write my own minor mode.

The best part of using this approach is that when you want to check what the key bindings do in emacs' default configuration, you simply turn off your minor mode; you then turn it back on and you get back your custom key bindings.

;; Main use is to have my key bindings have the highest priority
;; https://github.com/kaushalmodi/.emacs.d/blob/master/elisp/modi-mode.el

(defvar my-mode-map (make-sparse-keymap)
  "Keymap for `my-mode'.")

;;;###autoload
(define-minor-mode my-mode
  "A minor mode so that my key settings override annoying major modes."
  ;; If init-value is not set to t, this mode does not get enabled in
  ;; `fundamental-mode' buffers even after doing \"(global-my-mode 1)\".
  ;; More info: http://emacs.stackexchange.com/q/16693/115
  :init-value t
  :lighter " my-mode"
  :keymap my-mode-map)

;;;###autoload
(define-globalized-minor-mode global-my-mode my-mode my-mode)

;; https://github.com/jwiegley/use-package/blob/master/bind-key.el
;; The keymaps in `emulation-mode-map-alists' take precedence over
;; `minor-mode-map-alist'
(add-to-list 'emulation-mode-map-alists `((my-mode . ,my-mode-map)))

;; Turn off the minor mode in the minibuffer
(defun turn-off-my-mode ()
  "Turn off my-mode."
  (my-mode -1))
(add-hook 'minibuffer-setup-hook #'turn-off-my-mode)

(provide 'my-mode)

;; Minor mode tutorial: http://nullprogram.com/blog/2013/02/06/
5

To have a global binding override a major mode binding, simply set the binding to nil in the major mode:

(define-key my-major-mode-map (kbd "C-j") nil)

It's not possible to have the global binding take precedence over all modes in general (otherwise there would be no point in having major modes), but you could hack around it by creating your own minor mode with your most important bindings. Then you would at least have precedence over most (although not necessarily all) modes.

  • Will this work for any arbitrary major mode? In other words, if I install a major mode called "foo-mode" I can put (define-key foo-mode (kbd "C-j") nil) in my .emacs file and expect this to work? – nispio Sep 29 '14 at 20:44
  • Actually you'll want it to be foo-mode-map (my example in the answer was bad), but yes, that will disable the keybinding in the major mode so the global keybinding will be used instead (unless there's a different minor mode that's using it). – shosti Sep 29 '14 at 20:51
  • Is it fairly universal that the keymap for foo-mode will be called foo-mode-map? – nispio Sep 29 '14 at 20:58
  • @nispio yes, that's true for the vast majority of modes (although there are a few rogues out there). – shosti Sep 29 '14 at 21:15
0

You can use these macros:

(defmacro expose-global-keybinding (binding map)
  `(define-key ,map ,binding (lookup-key (current-global-map) ,binding)))

(defmacro expose-bindings (map bindings)
  `(dolist (bnd ,bindings)
     (expose-global-keybinding (kbd bnd) ,map)))

EDIT:

Check the example bellow:

If the keymap X is overriding your global binding Y, you write:

(expose-bindings X '("Y"))

And then the override will be 'undone'.

  • your macro you benefit from using the backquote: `(define-key ,map ,binding (lookup-key (current-global-map) ,binding)) – Sigma Sep 27 '14 at 3:44
  • Can you comment on what the macro is doing, and how to use it? It is not clear to me from the code. – nispio Sep 27 '14 at 5:47
  • The first macro just looks-up the key in the global map and assign the result to the other map, thus, exposing the global map binding through the other map. The second one just lets you apply the former for a list of bindings. – Renan Ranelli Sep 30 '14 at 6:24
  • Sorry if I am being dense, but I still don't completely understand the usage. Do I need to define my own special global keymap? Does it need to belong to a minor mode? Do I perform expose-bindings first, and then globally bind those keys to the commands that I want? Maybe you could show an example of what I might put in my init file to make this work. – nispio Oct 7 '14 at 14:33
  • 2
    Note that these macro names are misnomers. They do not "expose" the global binding, but rather they duplicate the global binding. If the real global binding changes, these duplicate copies will not. – phils Oct 8 '14 at 23:07

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.