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?

7
  • 3
    Are you interested in set-keymap-parent?
    – npostavs
    Commented Apr 6, 2017 at 1:53
  • @npostavs I get several errors like ` Wrong type argument: arrayp, menu-bar, Wrong type argument: arrayp, 8388703, Wrong type argument: arrayp, s-return`
    – erjoalgo
    Commented Apr 6, 2017 at 1:55
  • 1
    Regarding arrayp errors, you're missing a call to vector, try (lambda (key cmd) (define-key to-map (vector key) cmd)).
    – npostavs
    Commented Apr 6, 2017 at 3:05
  • 1
    Regarding multiple parents, see the bottom of (elisp) Inheritance and Keymaps
    – npostavs
    Commented Apr 6, 2017 at 3:05
  • 1
    @npostavs would you mind adding your suggestion as a solution?
    – erjoalgo
    Commented Apr 6, 2017 at 7:00

1 Answer 1

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.

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.