[Emacs] el-patchでパッケージにパッチを当てる


el-patchの紹介。

el-patchをつかうモチベーション

Emacsのパッケージを使っていて次のようなことはしばしば起こる。

  1. どうやらパッケージがバグっている
  2. issueはすでに起っており回避策が示されている
  3. init.el内で当該関数を上書きしてバグを回避

とりあえずこれで問題はないがパッケージのバージョンが変わったとき

  • 関数の内容が変わってしまったら
  • 関数の名前が変わってしまったら

init.el内で定義した関数は有効で有り続けるため非常にわかりにくい不具合が発生する恐れがある。

el-patchはこういった不安を軽減してくれるパッケージだ。ちなみに作者はstraight.elの作者さん。

インストール

straight.el + use-packageなら以下で良い

(use-package el-patch)

(setq straight-use-package-by-default t)の前に書いてしまいハマったのはご愛嬌。

使用例

「README.md見て」と言いたいところだが、ちょっととっつきにくく感じたので実際の使用例を記載してみる。

Emacs上でgoogle翻訳が使用できるgoogle-translateに以下のissueがある。
https://github.com/atykhonov/google-translate/issues/52

google-translateを使用するとSearch failed: ",tkk:'"というエラーが発生するのだが、回避策として
google-translate--search-tkkという関数を

(defun google-translate--search-tkk () "Search TKK." (list 430675 2721866130))

と置き換えてしまう回避策が示されている。

これをel-patchを用いて実行すると次のようになる。

(use-package google-translate
  ;; 中略
  :config/el-patch
  (el-patch-defun google-translate--search-tkk ()
    "Search TKK."
    (el-patch-swap
      (let ((start nil)
            (tkk nil)
            (nums '()))
        (setq start (search-forward ",tkk:'"))
        (search-forward "',")
        (backward-char 2)
        (setq tkk (buffer-substring start (point)))
        (setq nums (split-string tkk "\\."))
        (list (string-to-number (car nums))
              (string-to-number (car (cdr nums)))))
      (list 430675 2721866130)))
)

el-patchをインストールするとuse-package内で:config/el-patch(または:init/el-patch)が使えるようになるためそこで関数へのパッチをel-patch-defunで定義する。

ここではel-patch-swapで関数の内容をごっそり入れ替えてしまっている。これにより元は

(defun google-translate--search-tkk ()
  "Search TKK."
  (let ((start nil)
        (tkk nil)
        (nums '()))
    (setq start (search-forward ",tkk:'"))
    (search-forward "',")
    (backward-char 2)
    (setq tkk (buffer-substring start (point)))
    (setq nums (split-string tkk "\\."))
    (list (string-to-number (car nums))
          (string-to-number (car (cdr nums))))))

という関数だったものがissueで示された関数に置き換えられている。

修正前の関数も記述するのはel-patch-validate-allという関数でパッチ前の関数が変更されていたりしないか確認出来るようにするためだ。もしパッチ前の関数に変更があれば、以下のような警告が出力される。
(実はel-patch-defunでパッチ後の関数を定義するだけでも良いが必ず警告が出てしまうのでお勧めしない)

Warning (el-patch): Definition of defun `google-translate--search-tkk' differs from what is assumed by its patch

心配性な人はinit.elの最後に(el-patch-validate-all)を追加しておくとよいだろう。

おわりに

el-patchの大体の使用感はつかめたかと思う。
el-patchには上で紹介したもの以外にも様々な構文が用意されているため、あとはREADME.mdを読んで適切なものを使用してもらえれば良いと思う。