¿Cómo uso nadvice?

29

Mi configuración está llena de consejos, y sigo escuchando sobre el nuevo nadvice.elpaquete minimalista brillante .

He buscado en los manuales y he leído la fuente , pero admito abiertamente: todavía no tengo idea de cómo usarlo realmente.

¿Alguien aquí puede señalarme una guía o decirme cómo comenzar a portar mis consejos antiguos?

PythonNut
fuente
77
+1 para la pregunta. Si ha buscado los manuales y no encuentra lo que necesita, por favor considerar la presentación de un (doc) informe de error: M-x report-emacs-bug. Algunos desarrolladores a veces prefieren desarrollar en lugar de documentar. ;-) Es importante que Emacs se documente a sí mismo.
Drew
2
El manual en realidad tiene una sección sobre eso, ver (información "(elisp) Portar viejos consejos") . Sin embargo, no figura en el índice detallado por el motivo que sea.
wasamasa
3
Algunos ejemplos que utilizan nadvicede mi config: : después , : filtro de retorno , : alrededor , : antes, hasta que
Kaushal Modi
1
@wasamasa Me temo que esa sección está lejos de estar completa. Tengo algunos consejos (tal vez solo uno, ya veremos) que son más complejos. ¿Debo hacer una pregunta para cada uno aquí?
PythonNut

Respuestas:

22

Se incluye toda la información que necesita en la C-h f add-functionque se describe el mecanismo subyacente de advice-add.

El nuevo sistema de consejos básicamente actúa como reemplazar la definición actual de una función por la función descrita en la tabla en C-h f add-function, dependiendo de su elección del WHERE argumento, solo más limpio por el simple hecho de rastrear qué comportamiento se ha definido en qué archivo fuente.

Un ejemplo con la :aroundopción

El caso más general es la :aroundopción, así que doy un ejemplo para eso. (Probablemente sea mejor usar WHEREparámetros dedicados cuando sea posible, pero puede reemplazarlos por una :aroundfunción equivalente ).

A modo de ejemplo, digamos que usted desea depurar algún uso de find-file y desea printla lista de parámetros cada vez que se llama. Podrías escribir

(defun my-find-file-advice-print-arguments (old-function &rest arguments)
  "Print the argument list every time the advised function is called."
  (print arguments)
  (apply old-function arguments))

(advice-add #'find-file :around #'my-find-file-advice-print-arguments)

Con esta nueva implementación, todo lo que el consejo necesita se pasa como argumento. ad-get-argsse vuelve innecesario, porque los argumentos se pasan a la función de asesoramiento como argumentos de función normal (para WHEREargumentos para los que tiene sentido). ad-do-itse vuelve innecesario a medida que el :aroundconsejo obtiene como argumentos la función y los argumentos, por lo que (ad-do-it)se reemplaza por la forma

(apply old-function arguments)

o cuando hayas nombrado los argumentos

(funcall old-function first-arg second-arg)

que es más limpio ya que no hay formas mágicas involucradas. La modificación de los argumentos simplemente sucede al pasar valores modificados a OLD-FUNCTION.

Otros WHEREvalores

La cadena de documentos add-functioncontiene una tabla de todos los lugares de asesoramiento (o "combinadores"), y a qué son equivalentes, y explica la funcionalidad en términos de un lambdacomportamiento equivalente a la función recomendada:

`:before'       (lambda (&rest r) (apply FUNCTION r) (apply OLDFUN r))
`:after'        (lambda (&rest r) (prog1 (apply OLDFUN r) (apply FUNCTION r)))
`:around'       (lambda (&rest r) (apply FUNCTION OLDFUN r))
`:override'     (lambda (&rest r) (apply FUNCTION r))
`:before-while' (lambda (&rest r) (and (apply FUNCTION r) (apply OLDFUN r)))
`:before-until' (lambda (&rest r) (or  (apply FUNCTION r) (apply OLDFUN r)))
`:after-while'  (lambda (&rest r) (and (apply OLDFUN r) (apply FUNCTION r)))
`:after-until'  (lambda (&rest r) (or  (apply OLDFUN r) (apply FUNCTION r)))
`:filter-args'  (lambda (&rest r) (apply OLDFUN (funcall FUNCTION r)))
`:filter-return'(lambda (&rest r) (funcall FUNCTION (apply OLDFUN r)))

(cited from `C-h f add-function')

donde FUNCTION es la función de asesoramiento y OLDFUN la función donde se agrega el asesoramiento. No intente comprenderlos todos a la vez, solo seleccione un WHEREsímbolo que suene apropiado e intente comprenderlo.

O simplemente usar :around. Por lo que puedo decir, la única ventaja de usar WHEREs especializados :aroundpara todo es que obtienes un poco más de información al buscar C-h f ADVISED-FUNCTION antes de leer la cadena de documentación del consejo. A menos que planee publicar el código que contiene el consejo, probablemente no importe.

Funciones de asesoramiento con nombre

Recomiendo usar funciones con nombre como consejo ya que proporciona muchas ventajas (algunas de ellas también se aplican al uso de funciones con nombre para ganchos):

  • Se muestra C-h f find-filecomo

    :around advice: `my-find-file-advice-print-arguments'
    

    enlace a la definición de la función de asesoramiento, que como siempre contiene un enlace al archivo donde se definió. Si el consejo se hubiera definido como un lambdaformulario directamente en el advice-add formulario, la cadena de documentación se mostraría en línea (¿un desastre para cadenas de documentación largas?) Y nada indicaría dónde se definió.

  • Puedes eliminar el consejo con

    (advice-remove #'find-file #'my-find-file-advice-print-arguments)
    
  • Puede actualizar la definición del consejo sin volver a ejecutar advice-addo arriesgarse para mantener activa la versión anterior (ya que la ejecución advice-addcon un cambio lambdaserá reconocido como un nuevo consejo, no como una actualización de la anterior).

Comentario lateral La #'functionnotación es básicamente equivalente a 'function, excepto que ayuda al compilador de bytes a identificar símbolos como nombres de funciones y, por lo tanto, a identificar funciones que faltan (por ejemplo, debido a errores tipográficos).

kdb
fuente
Según la discusión que tuve con Stephen Monnier, las comillas no deben usarse aquí en todos los argumentos ... deberían serlo (advice-add 'find-file :around #'my-find-file-advice-print-arguments)y de manera similar (advice-remove 'find-file #'my-find-file-advice-print-arguments).
Kaushal Modi
Supongo que advice-addes un caso fronterizo. Personalmente, considero que la ' ↔ #'distinción es principalmente una ayuda para identificar errores tipográficos en los nombres de funciones, por lo que aquí probablemente dependerá de si uno espera que la función se defina para el momento en que se agrega el consejo.
kdb
@kdb Eventualmente descubrí esto por mí mismo (después de encontrarme con los documentos add-function). Deseo que los documentos lo aclaren más. Podría intentar hacer un parche para ello.
PythonNut
@kdb ¿Quieres decir "Aparece en C-h f find-file, no C-x?"
Peeja
@Peeja Sí, lo corrigió.
kdb