为现有Emacs模式添加键盘绑定
我正在尝试第一次修改Emacs。我录制了一个小的键盘宏,然后让Emacs把它输出成elisp,结果是:
(setq add-docstring
"\C-rdef\C-n\C-a\C-m\C-p\C-i\C-u6\"\C-u3\C-b")
(global-set-key "\C-c\C-d" 'add-docstring)
不过,在查阅Emacs的参考资料时,我发现C-c C-d在diff模式下已经被绑定了。我并不打算使用diff模式,但未来的事情谁也说不准,我不想给自己埋下一个陷阱。所以我希望这个快捷键只在python模式下有效,这样它可以帮助我添加文档字符串。
在我的/usr/share/emacs/23.whatever/list/progmodes
目录下,我找到了python.elc
和python.el.gz
。我解压了python.el.gz
,得到了一个可读的elisp文件。不过,现在文档对我来说变得难以理解了。
我该如何把我的快捷键绑定添加到python模式,而不是全局生效呢?
如果能在不重启emacs或关闭打开文件的情况下,将这些更改应用到python模式,那就更好了。我觉得作为一个自我修改的编辑器,这应该是有可能的。
2 个回答
首先,不要去修改python.el文件。你需要做的是为python模式创建自己的自定义快捷键。这通常是在你家目录下的.emacs
文件中完成的。
在这个文件中添加类似下面的内容(我没有测试过这个,所以可能会有语法错误,而且我自己也不使用python)
(add-hook 'python-mode-hook
'(lambda () (define-key python-mode-map "\C-c\C-d" 'add-doc-string)))
这里使用了钩子机制。每次你启动python模式时,这个函数都会被调用。这个函数只是把C-c C-d
这个快捷键绑定到你的add-doc-string
函数上。
这个回答很简短。可以去emacs的文档中了解一下关于.emacs文件、自定义设置和钩子的内容。
原来,C-c C-d 在 python-mode
中已经被绑定到 'python-pdbtrack-toggle-stack-tracking
这个功能上了,所以你可能需要重新考虑一下你的按键绑定选择。
注意:如果你只是想直接复制/粘贴一个解决方案,可以跳到答案的最后部分。继续阅读可以了解如何达到这个目的,以防你想再做一次。
这个宏是个不错的开始,但你现在的内容可能不太能用。要得到可以绑定到按键上的东西,可以试试 M-x insert-kbd-macro 来插入那个宏,你会得到:
(fset 'add-docstring
(lambda (&optional arg) "Keyboard macro." (interactive "p") (kmacro-exec-ring-item (quote ("def ...unprintable characters...6\"3" 0 "%d")) arg)))
(嗯……有些不可打印的字符,我不能直接复制粘贴到SO上,但你可以自己操作一下,得到正确的内容)。经过一些处理,你得到的结果相当于这个:
(fset 'add-docstring
(lambda (&optional arg)
"Keyboard macro."
(interactive "p")
(kmacro-exec-ring-item `(,(kbd "C-r def C-n C-a C-m C-p C-i C-u 6 \" C-u 3 C-b") 0 "%d")
arg)))
这就是第一步。通过上面的内容,你可以使用 M-x add-docstring
来获得你想要的功能。
下一步就是你问的 - 如何在本地绑定按键。关于按键绑定的文档可以在这里找到,你需要关注的是本地键映射部分,这部分内容会引导你到以下内容:
(add-hook 'python-mode-hook
(lambda () (define-key python-mode-map (kbd "C-c C-d") 'add-docstring)))
这段代码设置了一个匿名函数,当 python-mode
被开启时会被调用,而这个函数只做一件事 - 在专门为 python-mode 设置的键映射中设置你想要的按键绑定。
如果你仔细阅读键映射部分,你会发现 Emacs 遵循一个约定,只有用户可以将命令绑定到 C-c a,其中 a 可以是任何大小写字母(例如 C-c d、C-c T、C-c p 都可以使用),而软件包则将特定于模式的绑定限制在 C-c %,其中 % 是任何标点符号或控制键(例如 C-c C-c、C-c [、C-c C-z)。
所以,如果你把绑定改成 C-c d,那么几乎可以保证不会和其他软件包冲突。Emacs 自带的 python.el
也遵循这些约定,大多数(几乎所有?)随 Emacs 一起发布的软件包也是如此。
你会注意到我使用kbd
来读取按键序列。这种方式更通用,我觉得更容易阅读。
你还可以做一些进一步的清理工作:
- 把自定义内容放在一个命名函数中
- 用 elisp 重写宏
- 使用
eval-after-load
代替钩子(见这个问题)
这是我对第一个建议的做法,这样可以给你一个方便的地方来放其他自定义内容:
(add-hook 'python-mode-hook 'my-python-customizations)
(defun my-python-customizations ()
"set up my personal customizations for python mode"
;; put other customizations in here
(define-key python-mode-map (kbd "C-c C-d") 'add-docstring))
(defun add-docstring (&optional arg)
"Keyboard macro."
(interactive "p")
(kmacro-exec-ring-item `(,(kbd "C-r def C-n C-a C-m C-p C-i C-u 6 \" C-u 3 C-b") 0 "%d")
arg))
使用命名函数会更整洁,因为你可以在需要时执行 (remove-hook 'python-mode-hook 'my-python-customizations)
来移除它。此外,如果你查看钩子的值(C-h v python-mode-hook RET),就能清楚地看到调用了什么(匿名函数比较长,不容易阅读)。
为了额外的奖励,在你把代码粘贴到 .emacs
文件后,执行 M-x eval-region,这会告诉 Emacs 评估该区域的代码。要查看你现有的 python 缓冲区中的更改,你只需打开一个新的 python 文件/缓冲区,这样就会触发按键绑定的更改 - 这对所有 python 缓冲区都是通用的。
祝你编程愉快。