在之前的内容中,介绍了可以存储keymap绑定的几个位置,并且说明了查找的顺序。同时我们也提到绑定到minor mode 中的快捷键只有在minor mode 被激活时才有能生效。那么Emacs 是如何管理 minor mode 中的快捷键的呢?
背景知识
Emacs 为所有minor mode 维护了一张公共的表—— minor-mode-map-alist。它内部的元素结构为:
((minor-mode-variable . keymap) ...)前面是一个minor-mode 的变量,你可以简单的理解为:
(
(company-mode . company-active-map)
(flyspell-mode . flyspell-mode-map)
(some-mode . some-mode-map)
)虽然实际的数据结构会更加复杂,但是为了方便理解,可以暂时认为它就是这么组织的。当一个 minor mode 被激活时,它的变量部分就被设置为激活状态。在实际查找时会沿着表中的顺序依次查找那些激活的项中是否有对应的绑定。
为minor mode 创建新的绑定
从上面我们就能看出,如果我们要往特定的 minor mode 中新建自己的快捷键,该如何做呢?
如果是一个新建的 minor mode,我们可以先创建一个 keymap,然后通过 define-minor-mode 宏中的 :key 进行关联。define-minor-mode 是用来创建新的minor mode 。它的具体内容等我们介绍 minor mode 的时候讲解。
(defvar my-mode-map
(let ((map (make-sparse-keymap)))
(define-key map (kbd "C-c m") #'my-command)
(define-key map (kbd "C-c n") #'my-other-command)
map))
;; 创建新的 mode 并且绑定
(define-minor-mode my-mode
"My minor mode."
:lighter " My"
:keymap my-mode-map)
;; 激活mode
(my-mode 1)如果是已经存在的 minor-mode ,我们可以通过获取mode中的keymap变量,然后通过 keymap-set 传入这个变量进行绑定。一般约定俗称的规矩是 mode 名称后加上 map 就是它的 keymap。例如 my-mode 里面的 keymap 一般命名为 my-mode-map。但是这个并不是强制的,所以更稳妥做法是先看看是否有 my-mode-map 变量的文档,看看说明是否是该mode的map。
更稳妥的做法是查找 minor-mode-map-alist 变量。对于这种键值对的结构,我们可以使用 assq 通过键来匹配值。
(assq 'company-mode minor-mode-map-alist)如果返回 (company-mode . company-mode-map) 后续我们就可以拿 company-mode-map 来进行操作了。
解决快捷键重复的问题
我们说实际上每个minor mode 都有一个自己的keymap, 保存在 minor-mode-map-alist,只有在对应的 minor mode 启用时才生效。这样就可能出现一种情况:如果两个minor 拥有同一个快捷键绑定到不同的命令,但是又都处于激活状态,就出现了快捷键冲突,Emacs会根据它们在 minor-mode-map-alist 中的先后顺序进行匹配,这样在后面的快捷键永远不会被匹配到了。
我们可以先删除 mode 在 minor-mode-map-alist 中对应的项,然后利用 push 将原来删除的项插入alist 的顶部。
(let ((cell (assq mode-var minor-mode-map-alist)))
(when cell
;; 1. 先从列表中移除
(setq minor-mode-map-alist (assq-delete-all mode-var minor-mode-map-alist))
;; 2. 重新推入最头部
(push cell minor-mode-map-alist)))或者我们也可以对于另一个mode中不想要的映射执行 keymap-set 将快捷键绑定的函数置空。
练习
本次的练习,我们尝试创建一个windows-notepad-mode 绑定一些Windows 文本编辑器的快捷键,将Windows的操作习惯移植到Emacs中。例如 C-c 、C-v、C-x、C-z 这类经典的快捷键绑定到具体命令。
(defvar windows-notepad-mode-map
(make-sparse-keymap))
(keymap-set windows-notepad-mode-map "C-a" #'mark-whole-buffer) ;;全选
(keymap-set windows-notepad-mode-map "C-c" #'kill-ring-save) ;;拷贝
(keymap-set windows-notepad-mode-map "C-s" #'save-buffer) ;;保存
(keymap-set windows-notepad-mode-map "C-v" #'yank) ;;粘贴
(keymap-set windows-notepad-mode-map "C-z" #'undo) ;;撤销
(keymap-set windows-notepad-mode-map "C-x" #'kill-region) ;;剪切
(define-minor-mode windows-notepad-mode
"Windows notepad mode"
:lighter " 已开启Windows快捷键绑定" ;;提示信息
:keymap windows-notepad-mode-map )
(windows-notepad-mode)
评论 (0)