《elisp 每日一练》—— 前缀按键

《elisp 每日一练》—— 前缀按键

Masimaro
2026-08-25 / 0 评论 / 0 阅读 / 正在检测是否收录...

上一节我们提到Emacs中keymap上某个键可以绑定一个具体的函数,也可以绑定另一个keymap,如果绑定另一个keymap,它就变成了一个前缀键,本篇我们来讨论前缀和自建keymap

背景知识

(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 绑定的函数,最终执行这个函数。我们可以用这个图来描述这个树形结构

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-file
  • C-c p b 绑定到 switch-buffer
  • C-c p g s 绑定到 vc-dir
  • C-c p g l 绑定到 vc-print-log
(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-keymap
  • define-prefix-command
0

评论 (0)

取消