0

If I have a key binding I want to apply to multiple keymaps, for example.

(evil-define-key 'visual c-mode-map (kbd "C-M-?") 'c-comment-from-c++)
(evil-define-key 'visual c++-mode-map (kbd "C-M-?") 'c-comment-from-c++)
(evil-define-key 'visual glsl-mode-map (kbd "C-M-?") 'c-comment-from-c++)

I tried putting the keymaps into a list and calling evil-define-key with each but it didn't work.

eg:

(dolist (this-map (list c-mode-map c++-mode-map glsl-mode-map))
  (evil-define-key 'visual this-map (kbd "C-M-?") 'c-comment-from-c++))

Is this possible?

1
  • In this particular case, all three modes call the hook c-mode-common-hook. So you could do something like (add-hook 'c-mode-common-hook 'my-c-mode-common-hook), with my-c-mode-common-hook containing (local-set-key (kbd "C-M-?") 'c-comment-from-c++)
    – Tyler
    Mar 15, 2021 at 13:28

3 Answers 3

1

In this kind of situation it's useful to use the function equivalent of whatever macro you're wrestling with, evil-define-key*. Note that this evaluates the keymap argument instead of deferring it to a later point, so I've adjusted your code to be run after the maps have been defined for c-mode and c++-mode:

(with-eval-after-load 'cc-mode
  (dolist (this-map (list c-mode-map c++-mode-map))
    (evil-define-key* 'visual this-map (kbd "C-M-?") 'c-comment-from-c++)))
0

(You don't say what you mean by "it didn't work". Error?)

I don't use Evil, and I don't have its source code. So this probably won't help. Anyway...

Googling a bit, this doc page says that evil-define-key is a macro, not a function, which means it doesn't necessarily evaluate all of its arguments, and it returns Lisp code (the macro expansion), which then gets evaluated.

Even though it's a macro, that doc page indicates that it does evaluate all of its args ("The remaining arguments [besides STATE] are like those of define-key", which is a function), so it's not too clear why it's a macro. Presumably it does something else. (Or maybe that statement is just false.)

On the other hand, that page also kinda suggests that the KEYMAP argument is not evaluated, because it says "Note that foo-mode-map is unquoted, and that this form is safe before foo-mode-map is loaded.". So to me at least, that doc is not too clear.

A guess is that KEYMAP is not evaluated, which would mean that evil-define-key expects a keymap variable, that is, a symbol, for KEYMAP. That would explain why your code wouldn't work: because you're passing the values of those keymap variables, not the variables (symbols) themselves, to evil-define-key.

(Note that define-key does not accept a keymap variable as arg; it accepts only a keymap, that is, the value of a keymap variable, as arg.)

If this guess is correct, you could try quoting the keymap symbols: instead of (list c-mode-map c++-mode-map glsl-mode-map), try '(c-mode-map c++-mode-map glsl-mode-map).

Another thing that suggests this interpretation is the doc string mention that you can pass a quoted symbol instead of a keymap or keymap symbol, in which case the quoted symbol indicates a mode. This is no proof, but it does suggest that the arg isn't evaluated first.

Just a wild guess. No doubt some Evil user or someone with the definition of evil-define-key will give you a good answer.

(This answer might also help a bit. Dunno.)


OK, I found the source code and took a very quick peek at the definition of macro evil-define-key.

It seems you can pass either an (unquoted) keymap variable (symbol) or its value, and that evaluation is delayed in either case. My quick peek didn't tell me just what the problem is with your code, however...

0

Posting an answer since this is the final solution I came up with.

It uses macros to avoid duplication, defining a macro evil-define-key-for-cc-modes that handles the details.

(defmacro evil-define-key-with-modelist (state-arg mode-list-arg key-arg action-arg)
  `
  (let ((state ,state-arg)
        (mode-list ,mode-list-arg)
        (key ,key-arg)
        (action ,action-arg))
    (dolist (mode-item mode-list)
      (evil-define-key* state mode-item key action))))

(defmacro evil-define-key-for-cc-modes (state-arg key-arg action-arg)
  `
  (progn
    (with-eval-after-load 'cc-mode
      (evil-define-key-with-modelist
       ,state-arg
       (list c-mode-map c++-mode-map)
       ,key-arg
       ,action-arg))
    (with-eval-after-load 'glsl-mode
      (evil-define-key-with-modelist ,state-arg (list glsl-mode-map) ,key-arg ,action-arg))))

Example usage:

(evil-define-key-for-cc-modes 'visual (kbd "C-/") 'c-comment-from-cxx)

Your Answer

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

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