Come manipolare l'elenco degli argomenti in nadvice.el?


12

A seguito di una risposta a un'altra domanda sul nuovo sistema di consulenza :

In vecchio stile advice.el, era possibile manipolare singoli membri dell'elenco di argomenti di una funzione consigliata, senza fare affermazioni su quei membri non così manipolati. Ad esempio, il seguente consiglio:

(defadvice ansi-term (around prompt-for-name last)
  (let ((name (read-from-minibuffer "Tag: ")))
    (and (not (string= name ""))
         (ad-set-arg 1 (concat "Term: " name)))
    ad-do-it))

consente la fornitura (facoltativa) di un argomento buffer-name a una ansi-termchiamata, mentre ansi-termotterrà comunque il suo primo argomento chiedendo in base al proprio modulo interattivo.

(Per riferimento futuro, ansi-termla firma è (PROGRAM &optional BUFFER-NAME)e il suo modulo interattivo richiede PROGRAMMA con diverse possibili impostazioni predefinite, ma non fa nulla riguardo a BUFFER-NAME.)

Non sono sicuro se questo sia possibile o meno nadvice.el. Se lo è, non sono sicuro di come si possa fare. Ho trovato un paio di modi per sostituire l'elenco degli argomenti di una funzione consigliata.

Ad esempio, da * info * (elisp) Combinatori di consigli :

`:filter-args'
 Call FUNCTION first and use the result (which should be a list) as
 the new arguments to pass to the old function.  More specifically,
 the composition of the two functions behaves like:
      (lambda (&rest r) (apply OLDFUN (funcall FUNCTION r)))

Altri combinatori offrono capacità simili e il filo conduttore tra loro è che, mentre l'elenco di argomenti di una funzione può essere sostituito, troncato, esteso, ecc., Non esiste un modo apparente per i consigli di funzione di modificare l'argomento in una determinata posizione nell'elenco senza affermando qualcosa sul resto .

Nel caso in esame, sembra impossibile per l'autore del consiglio passare ansi-termsolo un nome di buffer, poiché non è possibile costruire un elenco che abbia un valore nella posizione 1 ma nulla, nemmeno nil, nella posizione 0. Nel caso generale, sembra impossibile per l'autore della consulenza modificare arbitrariamente argomenti oltre la posizione 0.

Ciò sembra sfortunato in quanto, per produrre un effetto simile, è necessario copiare e incollare il codice: in particolare, o posso copiare ansi-termil modulo interattivo ed estenderlo secondo i miei gusti, oppure posso copiarlo del ansi-termtutto ed estenderlo allo stesso modo. In entrambi i casi, ora devo ridefinire parte della distribuzione di Emacs Lisp nel mio file init, che mi sembra indesiderabile in termini sia di durata che di estetica.

La mia domanda, quindi, è: si può fare questo tipo di elenco degli argomenti con la manomissione nadvice.el? Se é cosi, come?


3
Perché non definisci il tuo comando interattivo al di sopra di un termine ansi? Penso che sia la soluzione preferibile qui.
lunaryorn,

1
Naturalmente non c'è nulla che mi impedisca di farlo, ma richiederebbe la sostituzione della parte migliore della memoria muscolare di un decennio, che vorrei evitare se potessi.
Aaron Miller,

Risposte:


5

Questo sembra sfortunato in quanto, per produrre un effetto simile, è necessario copiare e incollare il codice: [...] posso copiare ansi-termil modulo interattivo

Al contrario, penso che sarebbe una buona idea copiare e incollare la forma interattiva della funzione consigliata, anche se in realtà non è necessario farlo qui.

Ho letto la tua domanda dall'alto verso il basso. Quando sono arrivato al blocco di codice ho indovinato che il tuo consiglio probabilmente sta cambiando il nome del buffer. Ma non lo sapevo fino a quando non hai fornito la firma come commento.

Nel caso in discussione, sembra impossibile per l'autore del consiglio passare ansi-termsolo un nome di buffer, perché non è possibile costruire un elenco che abbia un valore nella posizione 1 ma nulla, nemmeno nil, nella posizione 0.

In effetti niente è meno niente di niente. :-) Ma non è rilevante qui.

Come puoi vedere nella documentazione che hai citato, il valore restituito dal consiglio viene utilizzato come argomento della funzione consigliata. Il valore restituito deve essere un elenco di tutti gli argomenti, non solo quelli che sono stati modificati.

Stare il più vicino possibile ai vecchi consigli, questo è quello che dovresti fare usando nadvice:

(defun ansi-term--tag-buffer (args)
  ;; As npostavs pointed out we also have to make sure the list is
  ;; two elements long.  Which makes this approach even more undesirable.
  (when (= (length args) 1)
    (setq args (nconc args (list nil))))
  (let ((name (read-from-minibuffer "Tag: ")))
    (and (not (string= name ""))
         (setf (nth 1 args) (concat "Term: " name))))
  args)

(advice-add 'ansi-term :filter-args 'ansi-term--tag-buffer)

Invece ti consiglio di definire un consiglio come questo:

(defun ansi-term--tag-buffer (program &optional buffer-name)
  (list program
        (let ((tag (read-from-minibuffer "Tag: ")))
          (if (string= tag "")
              buffer-name
            (concat "Term: " tag)))))

Questa variante in realtà è autoesplicativa.


Per la prima variante è necessario estendere l' argselenco in caso di una chiamata simile (ansi-term "foo"), altrimenti si (setf (nth 1 args)...genererebbe un errore.
npostavs,

Sì hai ragione. Un altro motivo per usare la seconda variante - la prima ha un bug ;-) Consente, a scopo dimostrativo, di supporre che buffer-namesia obbligatorio.
Tarsius

"Al contrario, penso che sarebbe una buona idea copiare e incollare la forma interattiva della funzione consigliata" - perché? Il codice di copia e incolla è una cattiva idea praticamente in ogni altro caso; Perché non qui?
Aaron Miller,

In realtà non penso che "copia-incolla" sia il termine giusto in questo caso, l'ho usato solo perché l'hai fatto. Ma anche se fosse appropriato usare quel termine qui, allora "non copiare e incollare" è solo una regola euristica e non assoluta. Altre euristiche, che a mio avviso si applicano qui, sono "dare nomi significativi a variabili e argomenti" e "quando hai una scelta tra complicare qualcosa o essere verboso, vai con i dettagli".
tarsius

1
In realtà, questo è ancora rotto, i :filter-argsconsigli ottengono un singolo argomento che è un elenco di argomenti per la funzione consigliata, quindi la prima variante dovrebbe cadere &reste la seconda variante dovrebbe usare una sorta di costrutto destrutturante per ottenere dei bei nomi.
npostavs,

3

Ecco come lo farei:

(defun my-ansi-term-prompt-for-name (orig-fun program
                                     &optional buffer-name &rest args)
  (apply orig-fun program
         (or buffer-name
             (let ((name (read-string "Tag: ")))
               (and (> (length name) 0)
                    (concat "Term: " name))))
         args))
(advice-add 'ansi-term :around #'my-ansi-term-prompt-for-name)

mentre sono stato io a presentarmi :filter-argspersonalmente lo trovo raramente conveniente.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.