Emacs 和大部分现代的代码编辑器不同,它除了是 GUI 编辑器还是一个 TUI 编辑器(或者说 GUI 的部分只是实现了一个特别的终端模拟器更合适)。所以当我出门使用 MacBook 但又要写 Linux 项目的时候,我发现直接 ssh 到 Linux 机器上调用 TUI 的 Emacs 比把代码同步过来再同步回去更加方便。但是很快我遇到一个奇怪的问题,只要我在 iTerm2 上移动鼠标,Emacs 里面就插入出现一串奇怪的编码一样的内容,然后只要切到别的窗口再切回 iTerm2,Emacs 就会插入一次 OI。如果是在 tmux 里面运行 Emacs 则不会有这个问题,让我百思不得其解。
我甚至关掉了 Emacs 对于终端鼠标事件的支持,但是 OIOI 还是存在,我开始怀疑这是 iTerm2 的鼠标支持的问题,于是我去 Emacs 中文群里面问了一下。群友 @Kana 建议我使用 emacs -Q -nw 测试一下会不会是我配置文件的问题,因为他之前遇到过同样的问题,原因是绑定了 M-[ 的快捷键,结果我试了一下还真是!我把 M-[ 绑定到了一个减少 tab stop 的函数上,注释掉这一行就好了。
既然知道问题出在哪里,原因也就很好分析了。终端里的复杂功能是通过使用 \e[ 开头的转义控制序列实现的,比如最常见的颜色输出就是通过 \e[ 开头后面跟上颜色编码实现,鼠标事件和窗口焦点切换也是。而 Emacs 有一个特性是如果你的键盘没有 Alt 键,那么你可以先按下 Esc 键再松开,然后再按下另一个键,就等价于按下 Alt 键和这个按键的组合键。恰好 \e 是 Esc,于是转义序列就被 Emacs 识别为一个 M-[ 的组合键然后执行了我绑定的函数,后面的编码就会被认为是普通字符插入到 buffer 里面。所以对于 \e[I 和 \e[O,看起来就好像 Emacs 在不停大喊 OIOIOI 一样。以前我是完全不考虑在终端里运行 Emacs 的,所以就忽略了这个问题。
PS 每年跨年之后我都会忘记要写新一年的年份,比如刚刚我就把这篇文章的日期写成了 2025 年 2 月 1 号然后思考为什么它没有出现在首页上。