用 Emacs 进行代码重构

  1. emacs

首先要解释一下这里提到的重构主要是指变量名、方法名的修改,并不涉及到任何代码优化的部分(Emacs can't help you with that )。

作为一名(前)Android 开发者,对于这部分基本是属于被 Intellj IDEA ( Android Studio) 惯坏了,右键点点就帮你换好了,顺便还帮你提前检查了可能出现的编译错误。当然这也是受益于 Java 是个静态语言,所以当我的主要工作转移到 Emacs 写 js(没错) 之后。虽然没办法用到这么方便的工具了,但是也让我学会了更加通用的办法,是好事(F91 语气)。

当前文件的“查找替换”: iedit

最常见的情况是当前编辑的文件中有需要修改的变量名,比如说一个 typo,由于自动补全工具的强大(比如说我在用的company-mode),如果你写错一次,那么多半这个 typo 基本就会到处都是了。

最容易想到的办法是使用 (replace-string) 查找替换,不过这样的局限性也很强。一是不够直观,二是在手动输入查找替换字符串中难免再次出错导致需要再来一瓶。

而使用 iedit ,只需要选中需要修改的字符串并执行 (iedit-mode): C-; ,当前 Buffer 所有的相同字符串都会高亮:

iedit-highlight.png

这时候修改高亮的字符串,其他的字符串也会同步修改。

iedit-changed.png

这个过程中随时都能看到全局的变化,也不容易再次犯错。

如果你觉得这样还不够清晰,也可以使用下文提到的 ivy-occur + wgrep 的方式修改,将搜索的范围限定到当前文件即可。

局部变量替换 narrowing

有些时候需要替换的是一个函数内部局部变量的名字,但是其他函数里面也有同名的变量我不想修改,这时候只用 iedit 的话就有点麻烦了,不过 Emacs 内置的 narrow 功能就派上了用场,需要注意的是这个功能默认是被禁用了,因为新手如果不小心按到快捷键会不知道发生了什么。第一次使用时 Emacs 弹出警告按下 y 即可。

简单来说, narrow 就是指限定当前编辑的区域,如果你按下 M-x 并输入 narrow<Tab> 会看到自动补全的三个方法分别是:

  1. narrow-to-defun (C-x n d)

这个方法一般只在 emacs-lisp-mode 有用,将当前编辑区域限定为当前的 (defun ()) 区域。

  1. narrow-to-page (C-x n p)

将当前编辑区域限定为当前的 page(?),关于 Page 相关的定义我还没有完全理解,根据我的 use case 基本跟 widen 的功能一致。

  1. narrow-to-region (C-x n n)

这个是我最常用到的,先选定一个区域然后将编辑区域限定为选定的区域。

当然还有恢复到完整显示的方法 widen (C-x n w)

处于 narrow 状态时,不管你怎么修改当前区域的内容都不会影响到文件的其余部分,所以就愉快的用 iedit 改掉局部变量的名字吧。

项目的查找替换 projectile & ivy & wgrep

最惨烈的情况当然是一个项目里多个文件里面都需要修改,(包括但不限于 leader 对你起的变量名不满意(x 等情况)。这里就需要一些额外的工具,包括和 ivy 。如果你还没用过 projectile 的话,建议花一点时间熟悉。另外 ivy 并不是必须的,不过非常推荐。

首先查找用 ivy 提供的 counsel-rg 配合 projectile

(defun projectile-smart-search()
  (interactive)
  (let ((text (substring-no-properties (or (thing-at-point 'symbol) ""))))
    (if (projectile-project-p)
        (counsel-rg text (projectile-project-root))
      (counsel-rg text))))

Tips: text 部分的作用是将光标位置所在的单词直接当成搜索字符串,不过这不重要。

当搜索结果出现在 minibuffer 之后,执行 (ivy-occur): C-x C-o 将所有查找结果在另一个 buffer 显示。

ivy-occur.png

如图的情况,假定我现在需要将 struct EmacsRime 改名为 ERime , 而另一个类型 EmacsRimeCandidates 不需要修改。我需要做的是将光标移动到 EmacsRimeCandidates 行执行 (ivy-occur-delete-candidate): C-d ,保证剩下的都是需要修改的:

ivy-occur-modified.png

使用 (ivy-wgrep-change-to-wgrep-mode): w 切换到 wgrep-mode 使用 iedit 完成修改。按下 C-c C-c 保存即可。

ivy-occur-changed.png

Tips: 使用 C-x s 可以一次性保存所有修改过的 buffer。

通用性

虽然这边举例都是用修改代码来说明,不过这并不意味着这些工具只能在编写代码时有用。比如我需要将本文中的 Emacs 全部替换为 Vim,我需要的操作仅仅是:

  1. 移动光标到其中一个 Emacs
  2. C-;
  3. M-d
  4. Vim
  5. C-;