Emacs ließ sich beraten


14

Ich möchte eine Funktion in einem Codeteil vorübergehend überschreiben.

Nehmen Sie zum Beispiel Folgendes:

(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)

Was geht nicht:

  • Dies. Es wäre viel sauberer, wenn ich vermeiden könnte, den Rat manuell zu aktivieren und zu deaktivieren, und darauf vertrauen könnte, dass sich Emacs um alles kümmert.
  • cl-letfIch kann die ursprüngliche Funktion nicht referenzieren, daher kann ich keine Dinge implementieren, :filter-argsdie normalerweise funktionieren würden.
  • cl-flet Funktionen in anderen Funktionen können nicht überschrieben werden.
  • nofletist ein externes Paket, das ich vermeiden möchte. (Tut auch viel mehr als ich brauche)

Antworten:


16

Könnten Sie nicht verwenden, (cl-)letfwährend Sie die ursprüngliche Funktion selbst referenzieren?

Etwas wie das:

;; 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)



Sie können dies auch in ein Makro einschließen, wenn Sie es wiederverwenden möchten:

(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)))

Das obige Beispiel kann dann wie folgt umgeschrieben werden:

(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")

Danke, genau das habe ich gesucht. Das einzige, was ich ändern würde, ist die Verwendung cl-letf*für beide let.
PythonNut

Ich habe eine Makroversion hinzugefügt, die nur ein letf*Formular für beide Bindungen verwendet.
François Févotte

Nett! Ich könnte es benutzen, wenn ich den Rat an vielen Stellen brauche.
PythonNut
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.