byhc
2019 年10 月 4 日 05:43
1
假设我有以下函数定义:
(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 个赞
cireu
2019 年10 月 4 日 05:49
2
你以为会这样?
(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))
byhc
2019 年10 月 4 日 07:56
3
evil-define-key应该是一个宏,偿试过
(bind-key c++-mode-map)
并不起效
byhc:
(defun bind-key (keymap)
byhc:
(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。
以下例子都来自M-x ielm。
我原以为'(...)和(list ...)是一样的,但是现在发现他们不一样,后者造出的list似乎更纯:
ELISP> (car (list 1 2))
1 (#o1, #x1, ?\C-a)
ELISP> (car '(1 2))
1 (#o1, #x1, ?\C-a)
ELISP> (numberp (car (list 1 2)))
t
ELISP> (numberp (car '(1 2)))
t
ELISP> (keymapp (car (list ivy-mode-map swiper-map)))
t
ELISP> (keymapp (car '(ivy-mode-map swiper-map)))
nil
原以为quote只quote一层,似乎不对:
ELISP> (car (car '((define-key))))
define-key
ELISP> (car (car '('(define-key))))
quote
cireu
2019 年10 月 4 日 09:33
5
evil-define-key
改evil-define-key*
byhc
2019 年10 月 4 日 09:48
6
@cireu 嗯,换成evil-define-key*可以了,看来是宏的问题。多谢
byhc
2019 年10 月 4 日 09:51
7
evil-define-key是个宏,这种情况下,应该怎么传参呢?
cireu
2019 年10 月 4 日 10:09
8
原来的展开成这个,应该是卡了什么条件被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 个赞