Emacs cho phép lời khuyên


14

Tôi muốn tạm thời ghi đè một chức năng trong một đoạn mã.

Lấy ví dụ như sau:

(defun nadvice/load-quiet (args)
  (cl-destructuring-bind
      (file &optional noerror nomessage nosuffix must-suffix)
      args
    (list file noerror t nosuffix must-suffix)))

(defun nadvice/idle-require-quiet (old-fun &rest args)
    (advice-add 'load :filter-args #'nadvice/load-quiet)
    (apply old-fun args)
    (advice-remove #'load #'nadvice/load-quiet))

(advice-add 'idle-require-load-next :around #'nadvice/idle-require-quiet)

Những gì không hoạt động:

  • Điều này. Sẽ sạch sẽ hơn nhiều nếu tôi có thể tránh việc kích hoạt và vô hiệu hóa lời khuyên và tin tưởng vào bản chất đơn luồng của Emacs để lo mọi việc.
  • cl-letfsẽ không để tôi tham khảo chức năng ban đầu, vì vậy tôi không thể thực hiện những việc :filter-argsthường làm.
  • cl-flet không thể ghi đè các chức năng trong các chức năng khác.
  • nofletlà một gói bên ngoài, mà tôi muốn tránh. (Cũng làm nhiều hơn tôi cần)

Câu trả lời:


16

Bạn không thể sử dụng (cl-)letftrong khi tham khảo chức năng ban đầu?

Một cái gì đó như thế này:

;; Original function
(defun my-fun (arg)
  (message "my-fun (%s)" arg))


;; Standard call
(my-fun "arg") ;; => my-fun (arg)


;; Temporary overriding (more or less like an around advice)
(let ((orig-fun (symbol-function 'my-fun)))
  (letf (((symbol-function 'my-fun)
          (lambda (arg)
            ;; filter arguments
            (funcall orig-fun (concat "modified-" arg)))))
    (my-fun "arg")))
;; => my-fun (modified-arg)


;; The overriding was only temporary
(my-fun "arg") ;; => my-fun (arg)



Bạn cũng có thể gói cái này trong một macro nếu bạn định sử dụng lại nó:

(defmacro with-advice (args &rest body)
  (declare (indent 1))
  (let ((fun-name (car args))
        (advice   (cadr args))
        (orig-sym (make-symbol "orig")))
    `(cl-letf* ((,orig-sym  (symbol-function ',fun-name))
                ((symbol-function ',fun-name)
                 (lambda (&rest args)
                   (apply ,advice ,orig-sym args))))
       ,@body)))

Ví dụ trên có thể được viết lại như sau:

(defun my-fun (arg)
  (message "my-fun (%s)" arg))


(my-fun "my-arg")

(with-advice (my-fun
              (lambda (orig-fun arg)
                (funcall orig-fun (concat "modified-" arg))))
  (my-fun "my-arg"))

(my-fun "my-arg")

Cảm ơn, đây chính xác là những gì tôi đang tìm kiếm. Điều duy nhất tôi thay đổi là sử dụng cl-letf*cho cả hai let.
PythonNut

Tôi đã thêm một phiên bản macro chỉ sử dụng một letf*hình thức cho cả hai ràng buộc.
François Févotte

Đẹp! Tôi có thể sử dụng nó nếu cuối cùng tôi cần lời khuyên ở nhiều nơi.
PythonNut
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.