Disclaimer:
- I am not an
evil
user so my understanding of evil-normal-state-map
may be inaccurate
- There exist naughty Emacs modes in the wild which do not follow all of the standard conventions described in this answer; such cases can be considered bugs and ought to be disciplined
I'm looking for a function that automatically changes my current keybindings depending on the current major mode.
You're in luck! This is a fundamental design feature of Emacs modes[1] and their interplay with keymaps[2, 3]. The key to achieving your desired behaviour simply lies in knowing which keymap to customise[4] and when / how[5].
In summary of the manual nodes linked in the footnotes, it is useful to note:
- Every buffer has a single
major-mode
and any number of minor modes enabled at a time
- All modes are (generally) independent of one another and of any other buffer's modes
- Enabling a major mode in a buffer automatically enables its associated keymap for that buffer[6]; this is also true of some, but not all, minor modes
- Enabling any mode runs its dedicated mode hook[7]
AIUI, evil-normal-state-map
is the keymap of a global minor mode, meaning it can be (and usually is) active in multiple buffers, irrespective of their major-mode
. Furthermore, though keymaps of both major and minor modes may not always be active, the variable that holds them (in this case evil-normal-state-map
) is a global definition. This means that modifying evil-normal-state-map
(from any buffer) will affect all buffers which have that minor mode enabled. This explains your reported behaviour when switching between buffers. See (elisp) Active Keymaps
for a more detailed and technical explanation of this.
So, in order to bind the same key to different commands in org-mode
and clojure-mode
buffers, you should instead be customising the local (major mode) keymap in the corresponding mode hook:
(add-hook 'org-mode-hook
(lambda ()
(local-set-key (kbd "C-c r") #'mark-done-and-archive)))
(add-hook 'clojure-mode-hook
(lambda ()
(local-set-key (kbd "C-c r") #'cider-restart)))
which is equivalent to
(add-hook 'org-mode-hook
(lambda ()
(define-key org-mode-map (kbd "C-c r") #'mark-done-and-archive)))
(add-hook 'clojure-mode-hook
(lambda ()
(define-key clojure-mode-map (kbd "C-c r") #'cider-restart)))
This, however, will cause the same (redundant) operation to be performed every time one of these major modes is enabled. Since the two mode maps are global variables, a single modification after they are defined and before they are used for the first time will suffice:
(with-eval-after-load 'org
(define-key org-mode-map (kbd "C-c r") #'mark-done-and-archive))
(with-eval-after-load 'clojure-mode
(define-key clojure-mode-map (kbd "C-c r") #'cider-restart))
Footnotes
(emacs) Major Modes
(eintr) Keymaps
(elisp) Keymap Basics
(emacs) Rebinding
(emacs) Init Rebinding
(emacs) Local Keymaps
(emacs) Hooks
C-c
are taken from the major mode keymap instead of the evil's major mode keymap.evil-normal-state-map
, which is active globally. See the FAQ for some examples.