重新绑定按键的方式是修改其在按键映射中的条目。如果你在全局按键映射中修改了某个绑定,
该修改会在所有缓冲区生效(尽管在那些使用局部映射遮蔽全局绑定的缓冲区中,不会产生直接效果)。
如果你修改当前缓冲区的局部映射,这通常会影响所有使用同一主模式的缓冲区。
keymap-global-set 和 keymap-local-set 函数是执行这些操作的便捷接口
(see 按键绑定相关命令)。你也可以使用更通用的函数 keymap-set,
此时必须显式指定要修改的映射。
在为 Lisp 程序选择需要重新绑定的按键序列时,请遵循 Emacs 中各类按键的使用规范 (see Key Binding Conventions)。
如果 keymap 不是合法的按键映射,或 key 不是有效按键, 下述函数都会抛出错误。
key 是表示单个按键或一系列按键的字符串,且必须满足 key-valid-p。
按键之间以单个空格分隔。
每个按键可以是单个字符,或是用尖括号包裹的事件名称。 此外,任意按键前均可添加一个或多个修饰键。 最后,少数字符拥有专用的简写语法。以下是一些按键序列示例:
按键 f。
由 S、o、m 组成的三键序列。
先按带 Control 修饰的 c,再按 o 的两键序列。
带 Hyper 修饰的方向左键 left。
带 Meta 修饰的回车键 return。
同时带 Control 与 Meta 修饰的空格键 space。
拥有专用简写语法的按键仅有:NUL、RET、TAB、LFD、 ESC、SPC 和 DEL。
修饰键必须按字母顺序指定: ‘A-C-H-M-S-s’,对应顺序为 ‘Alt-Control-Hyper-Meta-Shift-super’。
该函数在 keymap 中为 key 设置绑定。
(若 key 长度超过一个事件,修改实际上会作用于从 keymap 可达的另一按键映射。)
参数 binding 可以是任意 Lisp 对象,但只有特定类型具有实际意义。
(有效类型列表参见 按键查找。)
keymap-set 返回的值为 binding。
若 key 为 <t>,则会设置 keymap 中的默认绑定。 当某个事件自身无绑定时,Emacs 命令循环会使用该按键映射的默认绑定(若存在)。
key 的每一级前缀都必须是前缀键(即绑定到一个按键映射)或未定义,
否则会抛出错误。若 key 的某一级前缀未定义,
keymap-set 会将其定义为前缀键,从而使 key 的后续部分可按指定方式定义。
若 keymap 中此前不存在 key 的绑定,新绑定会添加到 keymap 开头。 按键映射中绑定的顺序对键盘输入无影响,但对菜单按键映射至关重要(see 菜单按键映射)。
该函数是 keymap-set 的逆操作,它会取消 keymap 中 key 的绑定,
效果等同于将绑定设为 nil。若要彻底删除该绑定,可将 remove 指定为非 nil。
这仅在 keymap 存在父映射时存在区别:若仅在子映射中取消按键绑定,
该按键仍会遮蔽父映射中的同名按键;而使用 remove 则可使父映射中的对应按键生效。
注意:在 init 文件中,用户可以使用带非 nil 参数 remove 的 keymap-unset;
Emacs 扩展包应尽量避免使用该用法,因为扩展包本就完全掌控自身的按键映射,
不应修改其他包的按键映射。
下面的示例创建一个稀疏按键映射并在其中添加若干绑定:
(setq map (make-sparse-keymap))
⇒ (keymap)
(keymap-set map "C-f" 'forward-char)
⇒ forward-char
map
⇒ (keymap (6 . forward-char))
;; Build sparse submap for C-x and bind f in that.
(keymap-set map "C-x f" 'forward-word)
⇒ forward-word
map
⇒ (keymap
(24 keymap ; C-x
(102 . forward-word)) ; f
(6 . forward-char)) ; C-f
;; Bind C-p to thectl-x-map. (keymap-set map "C-p" ctl-x-map) ;;ctl-x-map⇒ [nil ... find-file ... backward-kill-sentence]
;; Bind C-f to foo in the ctl-x-map.
(keymap-set map "C-p C-f" 'foo)
⇒ 'foo
map
⇒ (keymap ; Note foo in ctl-x-map.
(16 keymap [nil ... foo ... backward-kill-sentence])
(24 keymap
(102 . forward-word))
(6 . forward-char))
可以看到,为 C-p C-f 设置新绑定实际上是修改了 ctl-x-map 中的条目,
这会同时改变默认全局映射中 C-p C-f 与 C-x C-f 的绑定。
keymap-set 是在按键映射中定义按键的通用工具。
但在编写模式时,常常需要一次性绑定大量按键,逐个使用 keymap-set 会繁琐且易出错。
此时可以使用 define-keymap,它会创建按键映射并批量绑定多个按键。
See 创建按键映射。
函数 substitute-key-definition 会遍历按键映射,
查找绑定到某一命令的按键并将其重新绑定到另一命令。
另一种更简洁且通常能达到相同效果的方式是将一个命令重映射为另一个命令
(see 命令重映射)。
该函数会将 keymap 中所有绑定到 olddef 的按键,
替换为绑定到 newdef。也就是说,olddef 出现的所有位置都会被替换为 newdef。
函数返回 nil。
例如,在使用默认绑定的 Emacs 中,以下代码会重新定义 C-x C-f:
(substitute-key-definition 'find-file 'find-file-read-only (current-global-map))
若 oldmap 非 nil,会改变 substitute-key-definition 的行为:
由 oldmap 中的绑定决定需要重新绑定哪些按键,而重绑定操作仍发生在 keymap 中,
而非 oldmap。这样你可以依据另一映射的绑定规则来修改当前映射。例如:
(substitute-key-definition 'delete-backward-char 'my-funny-delete my-map global-map)
这段代码会在 my-map 中,为所有全局绑定到标准删除命令的按键设置自定义删除命令。
以下示例展示了替换前后的按键映射:
(setq map (list 'keymap
(cons ?1 olddef-1)
(cons ?2 olddef-2)
(cons ?3 olddef-1)))
⇒ (keymap (49 . olddef-1) (50 . olddef-2) (51 . olddef-1))
(substitute-key-definition 'olddef-1 'newdef map) ⇒ nil
map ⇒ (keymap (49 . newdef) (50 . olddef-2) (51 . newdef))
该函数通过将 self-insert-command 重映射为 undefined 命令
(see 命令重映射),修改完整按键映射 keymap 的内容。
其效果是取消所有可打印字符的绑定,从而禁止普通文本插入操作。
suppress-keymap 返回 nil。
若 nodigits 为 nil,则 suppress-keymap 会将数字键定义为执行
digit-argument,- 定义为执行 negative-argument;
否则会将它们与其他可打印字符一样设为未定义。
suppress-keymap 并不会完全禁止修改缓冲区,
因为它不会禁用 yank、quoted-insert 等命令。
若要完全禁止修改缓冲区,应将其设为只读(see Read-Only Buffers)。
由于该函数会修改 keymap,通常只应在新建的按键映射上使用。
对已用于其他用途的现有映射进行操作可能引发问题;
例如,对 global-map 执行该操作会导致大部分 Emacs 功能无法使用。
该函数可用于初始化不希望插入文本的主模式的局部按键映射。
但这类模式通常应继承自 special-mode(see 基础主模式),
其按键映射会自动继承已被禁用的 special-mode-map。
special-mode-map 的定义方式如下:
(defvar special-mode-map
(let ((map (make-sparse-keymap)))
(suppress-keymap map)
(keymap-set map "q" 'quit-window)
...
map))