我是怎么在 Emacs 中进行重构的
好吧,只有写 Java 的人才会重构,我只是重命名而已。今天,由于 要发布 lispy 0.22.0 ,我不得不进行大量的重命名操作。下面我会分享我所用到的那些函数和 package。
My Github setup
我的仓库结构如下 (tree -da -L 1):
.
├── .cask
├── gh-pages
├── .git
└── images
我将 gh-pages 分支也作为独立的 git 仓库克隆到了原始的 lispy 仓库中了。这样我就可以同时重命名代码和文档中的函数了。
声明废弃的函数
可以用类似下面的方法来声明废弃的函数:
(define-obsolete-function-alias 'lispy-out-forward
'lispy-right "0.21.0")
(define-obsolete-function-alias 'lispy-out-backward
'lispy-left "0.21.0")
(define-obsolete-function-alias 'lispy-out-forward-nostring
'lispy-right-nostring "0.21.0")
这样在"0.21.0"及其以后的版本中, lispy-out-backward
作为 lispy-left
被废弃的别名,在调用时会有以下提醒:
`lispy-out-forward' is an obsolete command (as of 0.21.0); use `lispy-right' instead.
现在,在发布"0.22.0"版本时,我完全可以删掉这些别名的声明了. 毕竟我已经給他们予警告并预留了一个星期的时间来让他们把代码中的废弃函数名改为新的名字了。
修改文档中的废弃函数名
给予仓库的结果,我可以通过下面几步来进行:
Step 1: call rgrep
rgrep
是一个很好用的函数,我都觉得好奇怪,为什么默认没有为它分配一个快捷键呢; 考虑到我的键映射十分古怪,我将它的快捷键设置为 C-<
,这样实际上我要按下的键是 C-;-l
rgrep
函数接受三个参数:
- 要搜索的符号: 我将光标定位到
define-obsolete-function-alias
语句中要重命名的那个符号上,然后调用该rgrep
. 这样rgrep
会将该 symbol 名来作为搜索的默认正则表达式的值,而我要作的仅仅是按下回车就行了。 - 文件模式: 默认情况下使用的是当前文件的扩展名,即 *.el. 我输入的是
* RET
,因为我还要搜索 org 和 html 文件。 - 基线目录: rgrep 会在该目录下递归地搜索符合文件模式的文件; 这里的默认值为
~/git/lispy/
, 没问题,直接按RET
。
Step 2: call wgrep
wgrep 是一个很棒的 package,它本应该更加的流行的. 由于它与 wdired 这个 package 及其类似,因此我也将它的启动热键设为 C-x C-q
,退出热键设为 C-c C-c
:
(eval-after-load 'grep
'(define-key grep-mode-map
(kbd "C-x C-q") 'wgrep-change-to-wgrep-mode))
(eval-after-load 'wgrep
'(define-key grep-mode-map
(kbd "C-c C-c") 'wgrep-finish-edit))
Step 3: call iedit
iedit 是一个令人震惊的 package,它太 TMD 好用了. 我为它设置快捷键的配置如下所示,在开启了 iedit-mode 的情况下,你可以用 C-i
移动到下一个同名 symbol 出现的地方。
(global-set-key (kbd "C-M-i") 'iedit-mode)
为了更改 buffer 中所有搜索出的同名 symbol,我需要在按下 C-M-i
之前 mark 好所有我想修改的地方. 否则的话,iedit 会自动为搜索的 symbol 加上分隔标示(事实上,这本是一个很棒的功能), 这样的话 lispy-out-forward
和 lispy-out-forward 就不被 iedit 认为是匹配的了。
最终,我可以交互式的,一个字一个字的将所有的 lispy-out-forward
改为 lispy-right
. 这个体验类似于 multiple-cursors , (我也挺喜欢这个 package 的), 只是目的不一样而已。
Step 4: exiting
我做完这些修改之后:
- 按下
C-M-i
退出 iedit-mode - 按下
C-c C-c
退出 wgrep - 检查无误后保持被修改的文件,在没有保存前,你还可以将文件回退回原来的内容。
Outro
没错,但是请注意,这三个工具可以分开来完成各自不同的任务. 例如在我的另一个 refactoring demo 中,我使用 iedit-mode 来为一个 Elisp 中的 let-boud 变量解绑(该操作为 Common Lisp 也应该生效,毕竟这两者的符号都是一样的)。
拥有这种可以相互配合的工具是一件很棒的事情. 在大多数情况下,这笔只有一个 Rename 功能要好。例如若你只想在一个 buffe 的范围内重命名,则可以跳过 rgrep 和 wgrep 的步骤,而只使用 iedit-mode 就行了。而若修改的范围只是 bufer 的一部分,则我只需要:
- 按下
C-x nd
调用narrow-to-defun
函数 或者 按下C-x nn
调用narrow-to-region
函数(两个都对应 lispy 中的 N 键) - 调用
iedit-mode
- 按下
C-x nw
调用widen
(在 lispy 中按下 W 键)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

上一篇: 为 Emacs 增加新语言支持
下一篇: 我为什么从 Vim 叛逃到了 Emacs
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论