当 read-key-sequence 函数读取键序列时
(see 读取按键序列),它会使用 翻译键盘映射(translation keymaps)
将某些事件序列翻译为其他序列。翻译键盘映射
按优先级依次为 input-decode-map、
local-function-key-map 和 key-translation-map。
翻译键盘映射的结构与其他键盘映射相同, 但用途不同:它们用于在读取键序列时执行翻译, 而非为完整键序列绑定命令。每读取一个键序列时, 都会对照各翻译键盘映射进行检查。若某翻译键盘映射 将 k 绑定至向量 v,则每当 k 作为子序列出现在键序列 任何位置时, 该子序列都会被替换为 v中的事件。
例如,VT100 终端在按下小键盘键 PF1 时
会发送 ESC O P。在这类终端上,
Emacs 必须将该事件序列翻译为单个事件 pf1。
这一过程通过在 input-decode-map 中
将 ESC O P 绑定至 [pf1] 实现。
因此,当你在终端上按下 C-c PF1 时,
终端会发出字符序列 C-c ESC O P,
而 read-key-sequence 会将其翻译回
C-c PF1,并以向量 [?\C-c pf1] 形式返回。
翻译键盘映射仅在 Emacs 完成键盘输入解码后生效
(通过 keyboard-coding-system 指定的输入编码系统)。
See Terminal I/O Encoding。
该变量保存一个键盘映射,用于描述普通字符终端上 功能键发送的字符序列。
input-decode-map 的值通常根据终端的
Terminfo 或 Termcap 条目自动设置,但有时
需要终端专用的 Lisp 文件补充配置。
Emacs 内置了许多常见终端的专用文件;
其主要作用是在 input-decode-map 中添加
Termcap 与 Terminfo 无法自动推导的条目。
See Terminal-Specific Initialization。
该变量保存一个与 input-decode-map 类似的键盘映射,
区别在于它描述的是应被翻译为更优替代方案的键序列。
其生效顺序在 input-decode-map 之后、
key-translation-map 之前。
若 local-function-key-map 中的条目
与次要模式、局部或全局键盘映射中的绑定冲突,
则该条目会被忽略。即,重映射仅在原键序列
原本无任何绑定时才生效。
local-function-key-map 继承自 function-key-map。
若希望绑定在所有终端生效,应修改后者;
因此几乎总是优先使用前者。
该变量是另一个用途与 input-decode-map 相似的键盘映射,
用于将输入事件翻译为其他事件。
与 input-decode-map 的区别在于,
它在 local-function-key-map 翻译完成后才生效,
接收其翻译后的结果。
与 input-decode-map 相同、但与 local-function-key-map 不同,
无论输入键序列是否存在普通绑定,该键盘映射都会生效。
但需注意,实际键绑定仍可能影响 key-translation-map,
即使其会被翻译映射覆盖。
实际上,实际键绑定会覆盖 local-function-key-map,
从而可能改变 key-translation-map接收的键序列。
显然,应尽量避免此类情况。
key-translation-map 的设计意图是供用户
将一个字符集映射至另一个,包括通常绑定到
self-insert-command 的普通字符。
除简单别名外,你还可以在 input-decode-map、
local-function-key-map 和 key-translation-map 中
使用函数而非键序列作为键的翻译结果。
此时该函数会被调用,以计算该键的翻译结果。
键翻译函数接收一个参数,即 read-key-sequence 中指定的提示;
若键序列由编辑器命令循环读取,则为 nil。
大多数情况下可忽略该提示值。
若该函数自身读取输入,可能会改变后续事件。 例如,以下代码定义 C-c h, 将其后跟随的字符转换为带 Hyper 修饰键的字符:
(defun hyperify (prompt)
(let ((e (read-event)))
(vector (if (numberp e)
(logior (ash 1 24) e)
(if (memq 'hyper (event-modifiers e))
e
(add-event-modifier "H-" e))))))
(defun add-event-modifier (string e)
(let ((symbol (if (symbolp e) e (car e))))
(setq symbol (intern (concat string
(symbol-name symbol))))
(if (symbolp e)
symbol
(cons symbol (cdr e)))))
(keymap-set local-function-key-map "C-c h" 'hyperify)
键翻译函数可能需要根据包含非键盘事件的键序列
中的事件参数调整行为(see 输入事件)。
该信息可从变量 current-key-remap-sequence 获取,
在调用键翻译函数时,该变量会绑定到正在翻译的键子序列。
键序列的结束判定条件为:该键序列已绑定到某个命令, 或 Emacs 判定后续无任何事件可使其成为有效绑定的序列。
这意味着,尽管 input-decode-map 和 key-translation-map
无论原键序列是否有绑定都会生效,但已有绑定仍可能导致翻译无法进行。
例如,回到前面的 VT100 示例,若为全局映射添加 C-c ESC 的绑定,
则当用户按下 C-c PF1 时,Emacs 无法将 C-c ESC O P
解码为 C-c PF1,因为它会在 C-c ESC 后立即停止读取按键,
将 O P 留待后续处理。这是为了应对用户确实按下 C-c ESC 的情况,
此时 Emacs 不应等待下一个按键来判断用户按下的是 ESC还是PF1。
因此,应避免将命令绑定到以翻译键序列前缀为结尾的键序列。 主要易出问题的后缀/前缀包括 ESC、 M-O(实际为 ESC O)和 M-[(实际为 ESC [)。