5

The Emacs manual says:

Major modes customize Emacs by providing their own key bindings in local keymaps.... Minor modes can also have local keymaps...

Roughly, it seems to say:

Major modes provide local keymaps. Minor modes have local keymaps.

I understand the idea of major and minor modes: A buffer has one major mode and possibly (usually) several minor modes which apply small modifications.

I am confused about the terminology "local keymap". For a time I thought "major" and "local" were somewhat synonymous. This was confusing because "major" implies big and important and "local" implies small and less important.

I then looked at the rules for looking up keybindings which are roughly:

 (or (if overriding-terminal-local-map
         (find-in overriding-terminal-local-map))
     (if overriding-local-map
         (find-in overriding-local-map)
       (or (find-in (get-char-property (point) 'keymap))
           (find-in-any emulation-mode-map-alists)
           (find-in-any minor-mode-overriding-map-alist)
           (find-in-any minor-mode-map-alist)
           (if (get-text-property (point) 'local-map)
               (find-in (get-char-property (point) 'local-map))
             (find-in (current-local-map)))))
     (find-in (current-global-map)))

It looks like minor mode keymaps are found in minor-mode-map-alist and minor-mode-overriding-map-alist, and that major mode keymaps are found in current-local-map. All three of those are "local" variables, right? Which means any modifications to them are visible only to the current buffer. Do I understand that correctly?

If that is the case, then the keymaps do no belong to a major mode (or minor mode), they simply belong to buffer local variables.

Which brings me to my (somewhat vague) question: How are modes and keymaps associated?

To pick a specific example. How are emacs-lisp-mode, emacs-lisp-mode-map, and current-local-map related?

Is emacs-lisp-mode-map merely a template that initializes current-local-map? Is current-local-map an independent copy? Or do all buffers using emacs-lisp-mode share the same current-local-map?

2 Answers 2

6

You do have to be a bit careful with the terminology.

Each buffer has a single "local keymap", and therefore it's sensible to avoid using that terminology to refer to other keymaps.

current-local-map is a built-in function in `C source code'.

(current-local-map)

Return current buffer's local keymap, or nil if it has none.
Normally the local keymap is set by the major mode with `use-local-map'.

Other keymaps may indeed be active in the buffer on account of buffer-local values (such as buffer-local minor modes), but to reduce the potential for confusion you should avoid using the terms "local map" or "local keymap" to refer to anything other than (current-local-map), as that is the local map.

To pick a specific example. How are emacs-lisp-mode, emacs-lisp-mode-map, and current-local-map related?

Is emacs-lisp-mode-map merely a template that initializes current-local-map?

The define-derived-mode macro (which handles the boiler plate for most major modes) calls use-local-map with the major mode's keymap as an argument (and major modes defined without the macro will mostly do the same thing).

So in your example, calling emacs-lisp-mode means that (use-local-map emacs-lisp-mode-map) happens when the mode function is evaluated.

Is current-local-map an independent copy? Or do all buffers using emacs-lisp-mode share the same current-local-map?

Yes and no.

The value of the local map is not the major mode map's symbol, but the keymap value itself -- so emacs-lisp-mode-map is a global variable pointing at that structure, and then every buffer in emacs-lisp-mode has its own buffer-local "local map" value, which by default will also be pointing at that exact same structure. So (eq (current-local-map) emacs-lisp-mode-map) would be true in those buffers. If you were to call use-local-map in a buffer then you could point the local map to some other keymap, and it would no longer be the same as emacs-lisp-mode-map.


Tangentially, there's some other related behaviour in define-derived-mode. If the mode's keymap doesn't have a parent, then the macro will automatically assign one with (set-keymap-parent ,map (current-local-map)) -- which means that modes which derive from other modes will acquire the local bindings of their parent mode (as the parent mode's body has been evaluated already, and had itself set the current local map).

2
  • 1
    Local maps are always shared across all buffers under the same mode, there's no buffer-local major mode maps in Emacs. Changing the local-map via use-local-map changes the major mode map for all buffers under that same major mode.
    – Y.H Wong
    Commented Mar 3, 2021 at 10:58
  • 1
    You can trivially demonstrate to yourself that changing the local-map in some buffer with use-local-map has no effect on other buffers.
    – phils
    Commented Mar 3, 2021 at 11:11
0

Just do some test:

(defvar my-mode-map
  (let ((map (make-sparse-keymap)))
    (define-key map "A" 'funA)
    map))

(define-derived-mode my-mode fundamental-mode "MyMode")

The my-mode-map is a global variable, it bounds to a map object. Each buffer has its own local variable. In most cases, these local variables point to the same object as my-mode-map, which means: "Local maps are usually shared across all buffers under the same mode", except some one calls use-local-map explicitly.

(defun test ()
  (interactive)
  (generate-new-buffer "a")
  (generate-new-buffer "b")
  (generate-new-buffer "c")

  (message "buffer a")
  (set-buffer "a")
  (my-mode)
  (message "(current-local-map) = %s" (current-local-map))
  (message "my-mode-map = %s" my-mode-map)
  (message "eq? %s" (eq (current-local-map) my-mode-map))
  (define-key (current-local-map) "B" 'funB)
  (message "(current-local-map) = %s" (current-local-map))
  (message "my-mode-map = %s" my-mode-map)
  (message "eq? %s" (eq (current-local-map) my-mode-map))
  (define-key my-mode-map "C" 'funC)
  (message "(current-local-map) = %s" (current-local-map))
  (message "my-mode-map = %s" my-mode-map)
  (message "eq? %s" (eq (current-local-map) my-mode-map))

  (message "buffer b")
  (set-buffer "b")
  (my-mode)
  (message "(current-local-map) = %s" (current-local-map))
  (message "my-mode-map = %s" my-mode-map)
  (message "eq? %s" (eq (current-local-map) my-mode-map))
  (define-key (current-local-map) "D" 'funD)
  (message "(current-local-map) = %s" (current-local-map))
  (message "my-mode-map = %s" my-mode-map)
  (message "eq? %s" (eq (current-local-map) my-mode-map))
  
  (message "buffer a")
  (set-buffer "a")
  (message "(current-local-map) = %s" (current-local-map))
  (message "my-mode-map = %s" my-mode-map)
  (message "eq? %s" (eq (current-local-map) my-mode-map))

  (message "buffer c")  
  (set-buffer "c")
  (my-mode)
  (use-local-map (make-sparse-keymap))
  (message "(current-local-map) = %s" (current-local-map))
  (message "my-mode-map = %s" my-mode-map)
  (message "eq? %s" (eq (current-local-map) my-mode-map))
  
  (message "buffer b")
  (set-buffer "b")
  (message "(current-local-map) = %s" (current-local-map))
  (message "my-mode-map = %s" my-mode-map)
  (message "eq? %s" (eq (current-local-map) my-mode-map))
  )

The result:

-- in the same buffer, my-mode-map and (current-local-map) are the same
buffer a
(current-local-map) = (keymap (65 . funA))
my-mode-map = (keymap (65 . funA))
eq? t
(current-local-map) = (keymap (66 . funB) (65 . funA))
my-mode-map = (keymap (66 . funB) (65 . funA))
eq? t
(current-local-map) = (keymap (67 . funC) (66 . funB) (65 . funA))
my-mode-map = (keymap (67 . funC) (66 . funB) (65 . funA))
eq? t

-- in the different buffer, my-mode-map and (current-local-map) are the same
buffer b
(current-local-map) = (keymap (67 . funC) (66 . funB) (65 . funA))
my-mode-map = (keymap (67 . funC) (66 . funB) (65 . funA))
eq? t
(current-local-map) = (keymap (68 . funD) (67 . funC) (66 . funB) (65 . funA))
my-mode-map = (keymap (68 . funD) (67 . funC) (66 . funB) (65 . funA))
eq? t

buffer a
(current-local-map) = (keymap (68 . funD) (67 . funC) (66 . funB) (65 . funA))
my-mode-map = (keymap (68 . funD) (67 . funC) (66 . funB) (65 . funA))
eq? t

-- some one call use-local-map, which changes the local map
buffer c
(current-local-map) = (keymap)
my-mode-map = (keymap (68 . funD) (67 . funC) (66 . funB) (65 . funA))
eq? nil

-- which has no effect on other buffers
buffer b
(current-local-map) = (keymap (68 . funD) (67 . funC) (66 . funB) (65 . funA))
my-mode-map = (keymap (68 . funD) (67 . funC) (66 . funB) (65 . funA))
eq? t

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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