Jak korzystać z nadvice?

29

Moja konfiguracja jest pełna porad i wciąż słyszę o nowym błyszczącym minimalistycznym nadvice.elpakiecie.

Przeszukałem podręczniki i przeczytałem źródło , ale otwarcie przyznaję: wciąż nie mam pojęcia, jak go właściwie używać.

Czy ktoś tutaj może wskazać mi przewodnik lub powiedzieć, jak zacząć przekazywać porady w starym stylu?

PythonNut
źródło
7
+1 za pytanie. Jeśli szukałeś instrukcje i nie znaleźli Państwo potrzebne, należy rozważyć złożenie (DOC) raport o błędzie: M-x report-emacs-bug. Niektórzy programiści czasami wolą programować niż dokumentować. ;-) Ważne jest, aby sam Emacs dokumentował.
Drew
2
Podręcznik faktycznie zawiera sekcję na ten temat, patrz (informacje „(elisp) Przenoszenie starych porad”) . Jednak z jakiegokolwiek powodu nie jest wymieniony w szczegółowym indeksie.
wasamasa,
3
Kilka przykładów korzystające nadvicez mojego config: : po , : filtr zwrotny , : ok , : przed-aż
Kaushal Modi
1
@wasamasa Obawiam się, że ta sekcja jest daleka od ukończenia. Mam kilka porad (może tylko jedną, zobaczymy), które są bardziej złożone. Czy powinienem tutaj zadać pytanie?
PythonNut,

Odpowiedzi:

22

Wszystkie potrzebne informacje są zawarte w tym, C-h f add-functionktóry opisuje podstawowy mechanizm advice-add.

Nowy system porad działa w zasadzie jak zastąpienie bieżącej definicji funkcji funkcją opisaną w tabeli, w C-h f add-functionzależności od wyboru WHERE argumentu, czystsze tylko w celu śledzenia, jakie zachowanie zostało zdefiniowane w danym pliku źródłowym.

Przykład z :aroundopcją

Najbardziej ogólnym przypadkiem jest :aroundopcja, więc podaję na to przykład. (Prawdopodobnie lepiej jest użyć dedykowanych WHEREparametrów, jeśli to możliwe, ale można zastąpić się równoważnymi :aroundfunkcjami).

Na przykład, powiedzmy, że chcesz debugować użycie find-file i printlistę argumentów za każdym razem, gdy jest wywoływana. Mógłbyś pisać

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

Dzięki tej nowej implementacji wszystko, czego potrzebuje rada, jest przekazywane jako argument. ad-get-argsstaje się niepotrzebny, ponieważ argumenty są przekazywane do funkcji porady jako zwykłe argumenty funkcji (dla WHEREargumentów, dla których ma to sens). ad-do-itstaje się niepotrzebny, ponieważ :aroundrada otrzymuje jako argumenty funkcję i argumenty, dlatego (ad-do-it)zostaje zastąpiona przez formę

(apply old-function arguments)

lub gdy podasz argumenty

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

co jest czystsze, ponieważ nie ma w tym żadnych magicznych form. Modyfikacja argumentów następuje po prostu przez przekazanie zmodyfikowanych wartości do OLD-FUNCTION.

Inne WHEREwartości

Dokument add-functionzawiera tabelę wszystkich miejsc na porady (lub „kombinatorów”) oraz ich odpowiedników i wyjaśnia funkcjonalność w kategoriach lambdazachowania równoważnego z zalecaną funkcją:

`: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')

gdzie FUNKCJA jest funkcją porady, a OLDFUN funkcja, do której dodawana jest opinia. Nie próbuj zrozumieć ich wszystkich naraz, po prostu wybierz WHEREsymbol, który brzmi dobrze i spróbuj go zrozumieć.

Lub po prostu użyj :around. O ile mogę powiedzieć jedyną zaletą korzystania specjalizuje WHEREs nad :aroundwszystkiego jest to, że można uzyskać nieco więcej informacji z patrząc C-h f ADVISED-FUNCTION przed przeczytaniu docstring o poradę. O ile nie planujesz opublikować kodu zawierającego porady, prawdopodobnie nie ma to znaczenia.

Nazwane funkcje porad

Polecam używanie nazwanych funkcji jako porad, ponieważ zapewnia ono wiele korzyści (niektóre z nich dotyczą także używania nazwanych funkcji do przechwytywania):

  • Pojawia się C-h f find-filejako

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

    link do definicji funkcji porady, która jak zwykle zawiera link do pliku, w którym została zdefiniowana. Gdyby rada została zdefiniowana jako lambdaforma bezpośrednio w advice-add formie, dokumentacja byłaby pokazana w linii (bałagan dla długich dokumentów?) I nic nie wskazywałoby, gdzie została zdefiniowana.

  • Możesz usunąć poradę za pomocą

    (advice-remove #'find-file #'my-find-file-advice-print-arguments)
    
  • Możesz zaktualizować definicję porady bez ponownego uruchamiania advice-addlub ryzykowania, że ​​stara wersja pozostanie aktywna (ponieważ uruchomienie advice-addze zmienioną wersją lambdazostanie rozpoznane jako nowa porada, a nie jako aktualizacja starej).

Side uwaga#'function notacja jest w zasadzie równoznaczne 'functionz tym, że to pomoże kompilator bajt identyfikacji symboli jako nazw funkcji i tym samym zidentyfikować brakujące funkcje (na przykład z powodu literówek).

kdb
źródło
Zgodnie z dyskusją, którą prowadziłem ze Stephenem Monnierem, cytatów z haszowaniem nie powinno się tutaj używać we wszystkich argumentach ... powinno być (advice-add 'find-file :around #'my-find-file-advice-print-arguments)i podobnie (advice-remove 'find-file #'my-find-file-advice-print-arguments).
Kaushal Modi,
Myślę, że advice-addto sprawa graniczna. Osobiście uważam to ' ↔ #'rozróżnienie za głównie pomoc w identyfikacji literówek w nazwach funkcji, więc tutaj prawdopodobnie będzie to zależeć od tego, czy ktoś spodziewa się zdefiniowania funkcji do czasu dodania porady.
kdb,
@kdb W końcu się o tym dowiedziałem (po tym, jak natknąłem się na dokumenty add-function). Chciałbym, żeby doktorzy to wyjaśnili. Może będę chciał to zrobić.
PythonNut,
@kdb Czy masz na myśli „To się pojawia C-h f find-file, prawda C-x?
Peeja,
@Peeja Tak, poprawiłem.
kdb