psearch: 基于 pcase 的 elisp 代码搜索工具

看到 GitHub 上有一个希望 patch 后能自动关闭打开的 buffer 的 issue,由于我高强度使用前辈的包,所以感觉还是挺需要这个功能的。

我目前初步的实现如上代码:

diff --git a/psearch.el b/psearch.el
index dab0721..1d09e88 100644
--- a/psearch.el
+++ b/psearch.el
@@ -793,11 +793,21 @@ For example:
 
 See `psearch-patch' for explanation on arguments ORIG-FUNC-SPEC and PATCH-FORM."
   (declare (indent 2))
-  (let ((docpos (or psearch-patch-function-definition-docpos
-                    (if (symbolp orig-func-spec) 3 (length orig-func-spec)))))
-    `(let ((func-def (psearch-patch--find-function ',orig-func-spec)))
+  (let* ((docpos (or psearch-patch-function-definition-docpos
+                     (if (symbolp orig-func-spec) 3 (length orig-func-spec))))
+         (lib (cdr (condition-case nil
+                       (find-function-library name 'lisp-only t)
+                     (void-function nil))))
+         (file (when lib (file-truename (file-name-with-extension (find-library-name lib) "el"))))
+         (buffer (when file (get-file-buffer file))))
+    `(let ((_ (when (and ,file (not ,buffer))
+                (run-with-idle-timer 0 nil (lambda ()
+                                             (when (get-file-buffer ,file) (kill-buffer (get-file-buffer ,file)))))))
+           (func-def (psearch-patch--find-function ',orig-func-spec))
+           (lb lexical-binding))
        (with-temp-buffer
          ;; Modifiy function name
+         (setq lexical-binding lb)
          (when (and (memq (nth 0 func-def) '(defun defsubst)) (not (eq ',name (nth 1 func-def))))
            (setcdr func-def (cons ',name (nthcdr 2 func-def))))
          (print func-def (current-buffer))
@@ -818,10 +828,7 @@ See `psearch-patch' for explanation on arguments ORIG-FUNC-SPEC and PATCH-FORM."
          ;; Apply patch
          (goto-char (point-min))
          (if (progn ,@patch-form)
-             (let* ((lib (cdr (condition-case nil
-                                  (find-function-library ',name 'lisp-only t)
-                                (void-function nil))))
-                    (buffer-file-name (if lib (file-name-sans-extension lib))))
+             (let ((buffer-file-name ,file))
                (eval-region (point-min) (point-max)))
            (signal 'psearch-patch-failed
                    (list ',orig-func-spec "PATCH-FORM not applied")))))))

关于 issue 中担心的问题:

  1. 判断一下 patch 前是否打开了源文件,如果打开了就不关闭;
  2. 关闭 buffer 只发生在 patch 执行完毕后(idle),所以连着 patch 同一个源文件的函数,不会造成反复打开和关闭的开销。

同时,在我自己的使用实践中,还发现有些代码,必须开启词法绑定才有效果,而 with-temp-buffer 默认是没有词法绑定的。因此我在上面的代码中,增加了词法绑定的设置。

在需要词法绑定的情形,这样即可:

(let ((lexical-binding t))
    (psearch-patch FUN
      (psearch-replace '`a '`b)))