上一节我们提到Emacs中keymap上某个键可以绑定一个具体的函数,也可以绑定另一个keymap,如果绑定另一个keymap,它就变成了一个前缀键,本篇我们来讨论前缀和自建keymap
背景知识
emacs-lisp
(defun my/hello()
(interactive)
(message "hello world"))
(keymap-local-set "C-c p h" 'my/hello)
我们先从上面的例子开始。上面我们通过 keymap-local-set 在当前buffer中绑定了一个快捷键。根据之前介绍的查找顺序,它在当前buffer的局部keymap中找到了这个绑定。
而我们将它拆解开来,实际上它先找到 C-c ,发现它绑定了一个 keymap 实际上 C-c 就是一个前缀。然后在它绑定的 keymap 中找到了关于 p 的内容,p也绑定了一个 keymap,接着在这个 keymap 中找到了 h 绑定的函数,最终执行这个函数。我们可以用这个图来描述这个树形结构
txt
C-c
↓
keymap
↓
p
↓
keymap
↓
h → my/hello
也就是说我们调用 keymap-local-set 绑定快捷键时,Emacs已经帮我们构建了这样一个多层的keymap 结构。只不过这个过程我们之前没注意到。
既然Emacs已经默默帮我们构造了这样一个keymap,为什么还要提供让我们手动构造自己keymap的方式呢?因为这样可以使前缀的 keymap 成为一个独立的、可复用、可管理的对象,方便插件的开发者组织自己的快捷键。
例如 Projectile 有一组 C-c p 的快捷键,那么我们就可以创建一个keymap,将它绑定到 C-c p 的位置,然后我们只要操作这个 keymap ,就可以做到自然的将这些操作都绑定到 C-c p 下面
创建一个 keymap 可以使用 make-sparse-keymap。后续我们可以将它创建的 keymap 放入到 define-key 或者 keymap-set 函数的 keymap 参数中。
当然你也可以使用 define-prefix-command 这个宏来定义一个专门用于前缀的 keymap 。这个函数会将创建的keymap 映射为一个函数,后续我们可以调用该函数获得底层的 keymap。
练习
本节我们利用这两种机制来创建一个类似 Projectile 插件的一组以 C-c p 开头的函数。我们绑定这么几个函数:
C-c p f绑定到find-fileC-c p b绑定到switch-bufferC-c p g s绑定到vc-dirC-c p g l绑定到vc-print-log
emacs-lisp
(defvar my/git-sub-map (make-sparse-keymap)
"用于二级git菜单的keymap")
(define-prefix-command 'my/project-sparse-map)
;; 绑定一级菜单
(keymap-set my/project-sparse-map "f" #'find-file)
(keymap-set my/project-sparse-map "b" #'switch-buffer)
;; 绑定二级
(keymap-set my/git-sub-map "s" #'vc-dir)
(keymap-set my/git-sub-map "l" #'vc-print-log)
;; 将二级map绑定到一级map
(keymap-set my/project-sparse-map "g" my/git-sub-map)
;; 绑定一级菜单
(global-set-key (kbd "C-c p") my/project-sparse-map)
(message "my/project-sparse-map 内部结构: %S" my/project-sparse-map)
小结
本节我们简单了解了一下前缀和嵌套keymap的内容,一般来讲,创建一个keymap可以使用下面两个函数
make-sparse-keymapdefine-prefix-command