elisp 传参问题

假设我有以下函数定义:

(defun bind-key (keymap)
  (progn
    (evil-define-key '(normal insert) keymap (kbd "C-=") 'some-function)
    ))
(bind-key 'c++-mode-map)

现在调用始终不成功,evil-define-key似乎没进行按键绑定,这种情况应该怎么传参数?

配置文件中直接写:

(evil-define-key '(normal insert) c++-mode-map (kbd "C-=") 'some-function)

是可以的

1 个赞

你以为会这样?

(bind-key 'c++-mode-map)
;; Runs (evil-define-key '(normal insert) c++-mode-map (kbd "C-=") 'some-function)

函数会对参数求值,然后新建一个局部作用域,再执行函数体。'c++-mode-map求值就会得到c++-mode-map这个symbol。

evil-define-key也是个函数,同样,你配置里直接写的

(evil-define-key '(normal insert) c++-mode-map (kbd "C-=") 'some-function)

会对c++-mode-map求值,这个求值的结果就是c++-mode-map对应的变量,也就是keymap definition。

怎么改,直接用(bind-key c++-mode-map)

另外defun里没有必要用progn

(defun bind-key (keymap)
  (evil-define-key '(normal insert) keymap (kbd "C-=") 'some-function))

evil-define-key应该是一个宏,偿试过

(bind-key c++-mode-map)

并不起效

The parameter keymap evaluates to 'c++-mode-map, while evil-define-key expects something like c++-mode-map.

Which is a catch for newbees 因为我们 tend to 认为应该是define-key 'foo-map=》define-key拿着'foo-map这个symbol去记下“我在foo-map里面加了一个binding”,
而真相是define-key把这个binding写进了foo-map的值里面,something like (keymap (123 . next-line) (456 . next-line)),所以define-key拿到的需要是一个求值过的keymap,而不能是一个没法塞东西进去的'foo-map这种quoted symbol。

evil-define-keyevil-define-key*

@cireu 嗯,换成evil-define-key*可以了,看来是宏的问题。多谢

evil-define-key是个宏,这种情况下,应该怎么传参呢?

原来的展开成这个,应该是卡了什么条件被evil延迟绑定了,然而我自己不用evil,不想研究太多

(evil-delay
    '(and
      (boundp 'keymap)
      (keymapp keymap))
    '(condition-case-unless-debug err
      (evil-define-key*
       '(normal insert)
       keymap
       (kbd "C-=")
       'some-function)
      (error
       (message "error in evil-define-key: %s"
        (error-message-string err))))
  'after-load-functions t nil
  (format "evil-define-key-in-%s" 'keymap))

人家要什么你就传什么:要 keymap 就传 keymap,要变量就传变量。c++-mode-map 是一个变量,它的值是一个 keymap。一般函数和宏的参数用词就给出了提示,要变量的时候会写的,比如

(add-to-list LIST-VAR ELEMENT &optional APPEND COMPARE-FN)

这其实涉及到宏参数展开的问题。

当你把 keymap 变量传给 evil-define-key 宏,展开之后并不会得到变量指向的 'c++-mode-map,而是得到 keymap 字面本身。

以下代码反映的是同样的问题:

(defmacro def-greeting (name)
  "创建一个向 NAME 打招呼的函数."
  `(defun ,(intern (format "hello-%s" name)) ()
     (message "hello, %s" ',name)))
     
;; ---

(def-greeting foo)
;; => hell-foo

(hello-foo)
;; => "hello, foo"

;; ---

(let ((name 'bar))
  (def-greeting name))
;; => hello-name

(hello-bar)
;; => Symbol’s function definition is void: hello-bar

回到 evil-define-key,当你传递的是一个变量,它并没有立即绑定你的按键,而是创建了一个 evil-define-key-in-keymap 函数(注意函数的后缀 keymap,它就是你的 bing-key 参数名,如果改成 foobar,那么得到的函数就是 evil-define-key-in-foobar)。

evil-define-key 的实现看,evil-define-key-in-keymap 函数的内容是 nil,也就是什么都不干。

2 个赞