参考https://github.com/syl20bnr/spacemacs/blob/develop/core/core-keybindings.el,想整一个不依赖bind-map的键位设置。
具体的代码是
;; -- lexical-binding: t --
(defvar cabuda-default-keymap (make-sparse-keymap))
(defun cabuda/set-leader-keys (key def &rest bindings)
(while key
(define-key cabuda-default-keymap (kbd key) def)
(setq key (pop bindings)
def (pop bindings))))
(put 'cabuda/set-leader-keys 'lisp-indent-function 'defun)
(defun cabuda/init-major-leader-map (mode map)
(let ((hook (intern (format "%s-hook" mode))))
(or (boundp map)
(progn
(eval `(defvar ,map (make-sparse-keymap)))
;; (cabuda//setup-major-leader-map map hook)
(eval `(add-hook ,hook (lambda ()
(cabuda/set-leader-keys "m" (symbol-value ,map)))))
(boundp map))
)))
(defmacro cabuda//setup-major-leader-map (map hook)
`(add-hook ,hook (lambda ()
(cabuda/set-leader-keys "m" (symbol-value ,map)))))
(defun cabuda/set-major-leader-keys (mode key def &rest bindings)
(let ((map (intern (format "cabuda-%s-map" mode))))
(when (cabuda/init-major-leader-map mode map)
(while key
(define-key (symbol-value map) (kbd key) def)
(setq key (pop bindings)
def (pop bindings))))))
;; (defun cabuda/get-major-mode-keymap (mode)
;; (symbol-value (intern (format "cabuda-%s-map" mode))))
(define-key emacs-lisp-mode-map (kbd "C-c a") cabuda-default-keymap)
(cabuda/set-major-leader-keys 'emacs-lisp-mode "f" 'helm-find-files)
主要的疑问是:
使用宏可以满足我的要求,使用eval就不行。。请问是为什么呢?
没有吃糖。
`(add-hook ',hook
(lambda () (cabuda/set-leader-keys "m" (symbol-value ',map))))
我简化一下来说明问题:
(defun m-fn ()
(interactive)
(do-something))
(defmacro set-hook (hook map)
`(add-hook ,hook (lambda ()
(define-key (symbol-value ,map) (kbd "m") 'm-fn))))
(defun setup (hook map)
(eval `(defvar ,map (make-sparse-keymap)))
;; (eval `(add-hook ,hook (lambda ()
;; (define-key (symbol-value ,map) (kbd "m") 'm-fn)
;; )))
(set-hook hook map)
)
(let ((hook (intern "foo-hook"))
(map (intern "foo-map")))
(setup hook map))
(princ foo-hook)
;; => ((lambda nil (define-key (symbol-value map) (kbd m) 'm-fn)))
;; ^^^
首先来看宏。
使用看似宏可以通过,但是执行 (run-hooks 'foo-hook)
必然出错,因为最终生成的 lambda 里面的 map
是不存在的。应该把 map
的值先求出来,再代入:
(defmacro set-hook (hook map)
(let ((pam (symbol-value map)))
`(add-hook ,hook (lambda ()
(define-key ,pam (kbd "m") 'm-fn)))))
再来看 eval。
由于 eval 执行的时侯,变量 hook
和 map
都是有效的,所以 hook
可以直接使用,但 map
要先求值:
(eval `(add-hook hook (lambda ()
(define-key ,map (kbd "m") 'm-fn)
原先 (symbal-value ,map)
是错的,因为 ,map
已经求值了,不能对一个值再求值。
(eval `(add-hook ',hook (lambda ()
(cabuda/set-leader-keys "m" ,map))))
我试了下,这样也行
我试了下你的,还是不行。。是不是要设置lexical-binding: t ?
最终版,eval和宏都可以了
;; -*- lexical-binding: t -*-
(defvar cabuda-default-keymap (make-sparse-keymap))
(defun cabuda/set-leader-keys (key def &rest bindings)
(while key
(define-key cabuda-default-keymap (kbd key) def)
(setq key (pop bindings)
def (pop bindings))))
(put 'cabuda/set-leader-keys 'lisp-indent-function 'defun)
(defun cabuda/init-major-leader-map (mode map)
(let ((hook (intern (format "%s-hook" mode))))
(or (boundp map)
(progn
(eval `(defvar ,map (make-sparse-keymap)))
(cabuda//setup-major-leader-map map hook)
;; (eval `(add-hook ',hook (lambda ()
;; (cabuda/set-leader-keys "m" ,map))))
(boundp map))
)))
(defmacro cabuda//setup-major-leader-map (map hook)
`(add-hook ,hook (lambda ()
(cabuda/set-leader-keys "m" (symbol-value ,map))))
)
(defun cabuda/set-major-leader-keys (mode key def &rest bindings)
(let ((map (intern (format "cabuda-%s-map" mode))))
(when (cabuda/init-major-leader-map mode map)
(while key
(define-key (symbol-value map) (kbd key) def)
(setq key (pop bindings)
def (pop bindings))))))
(define-key emacs-lisp-mode-map (kbd "C-c a") cabuda-default-keymap)
(cabuda/set-major-leader-keys 'emacs-lisp-mode "f" 'helm-find-files)
使用宏的过程可以看作是先展开再eval
,但并不等价之,可以去了解下on lisp。