2

I am trying to write the following function:

(defun define-keymap-onto-keymap (from-map to-map)
  (map-keymap
   (lambda (key cmd) (define-key to-map key cmd))
   from-map))

My implementation above does not work in general, since the key argument to map-keymap is not a key, but an event, which is not always understood by define-key. For example:

(define-keymap-onto-keymap
 (let ((kmap (make-sparse-keymap)))
   (define-key kmap (kbd "<s-return>") nil)
   kmap)
 emacs-lisp-mode-map)
*** Eval error ***  Wrong type argument: arrayp, s-return

Also:

(define-keymap-onto-keymap
 (let ((kmap (make-sparse-keymap)))
   (define-key kmap (kbd "s-2") nil)
   kmap)
 emacs-lisp-mode-map)
*** Eval error ***  Wrong type argument: arrayp, 8388658

Is there a function similar to define-key, which understands whatever may be the type of key?

I am not interested in solutions like derived-mode-merge-keymaps, which use

(setcdr (nthcdr (1- (length new)) new) old)

which can result in infinite lists, and I'm not interested in a brand-new copy of from-map, like in keymap--merge-bindings but to add bindings to an existing to-map.

Can I just append from-map onto to-map?

erjoalgo
  • 863
  • 1
  • 5
  • 18

1 Answers1

5

You can get the effect you want by setting a keymap's parent, see (elisp) Inheritance and Keymaps

A keymap can inherit the bindings of another keymap, which we call the
"parent keymap".  Such a keymap looks like this:

     (keymap ELEMENTS... . PARENT-KEYMAP)

The effect is that this keymap inherits all the bindings of
PARENT-KEYMAP, whatever they may be at the time a key is looked up, but
can add to them or override them with ELEMENTS.

can a keymap have multiple parents?

   Sometimes you want to make a keymap that inherits from more than one
map.  You can use the function ‘make-composed-keymap’ for this.

 -- Function: make-composed-keymap maps &optional parent
     This function returns a new keymap composed of the existing
     keymap(s) MAPS, and optionally inheriting from a parent keymap
     PARENT.[...]

For example, here is how Emacs sets the parent of ‘help-mode-map’, such
that it inherits from both ‘button-buffer-map’ and ‘special-mode-map’:

     (defvar help-mode-map
       (let ((map (make-sparse-keymap)))
         (set-keymap-parent map
           (make-composed-keymap button-buffer-map special-mode-map))
         ... map) ... )

The main problem with your original code is that a binding passed to define-key must be a key sequence, see (elisp) Key Sequences):

A "key sequence", or "key" for short, is a sequence of one or more input
events that form a unit.  Input events include characters, function
keys, mouse actions, or system events external to Emacs, such as
‘iconify-frame’ (see Input Events).  The Emacs Lisp representation
for a key sequence is a string or vector.
[...]
   Key sequences containing function keys, mouse button events, system
events, or non-ASCII characters such as ‘C-=’ or ‘H-a’ cannot be
represented as strings; they have to be represented as vectors.

Whereas, map-keymap gives single events:

(map-keymap FUNCTION KEYMAP)

Call FUNCTION once for each event binding in KEYMAP.
FUNCTION is called with two arguments: the event that is bound, and
the definition it is bound to.  The event may be a character range.

So your code should look more like this:

(map-keymap
   ;; FIXME: also handle case where KEY is a range.
   (lambda (key cmd) (define-key to-map (vector key) cmd))
   from-map)

why don't I need the (vector key) when defining the key into the first map?

(kbd "<s-return>") gives [s-return], a vector.

npostavs
  • 9,203
  • 1
  • 24
  • 53