Wie man die Argumentliste in nadvice.el manipuliert?

12

Im Anschluss an eine Antwort auf eine andere Frage zum neuen Beratungssystem :

Im alten Stil advice.elwar es möglich, einzelne Mitglieder der Argumentliste einer empfohlenen Funktion zu manipulieren, ohne Aussagen zu denjenigen Mitgliedern zu machen, die nicht so manipuliert wurden. Zum Beispiel die folgenden Ratschläge:

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

Ermöglicht die (optionale) Bereitstellung eines Puffernamen-Arguments für einen ansi-termAufruf, wobei ansi-termdas erste Argument weiterhin durch Aufforderung gemäß seiner eigenen interaktiven Form erhalten wird.

(Zum späteren Nachschlagen ansi-termlautet die Signatur (PROGRAM &optional BUFFER-NAME)und das interaktive Formular fordert zur Eingabe von PROGRAM mit mehreren möglichen Standardwerten auf, hat jedoch keine Auswirkungen auf BUFFER-NAME.)

Ich bin mir nicht sicher, ob dies möglich ist oder nicht nadvice.el. Wenn es so ist, bin ich mir nicht sicher, wie es gemacht werden kann. Ich habe verschiedene Möglichkeiten gefunden, um die Argumentliste einer empfohlenen Funktion zu ersetzen .

Zum Beispiel von * info * (elisp) Hinweiskombinatoren :

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

Andere Kombinatoren bieten ähnliche Funktionen, und der gemeinsame Nenner besteht darin, dass die Argumentliste einer Funktion zwar ersetzt, abgeschnitten, erweitert usw. werden kann, es jedoch keine offensichtliche Möglichkeit für Funktionsempfehlungen gibt, das Argument an einer bestimmten Position in der Liste ohne zu ändern etwas über den Rest davon zu behaupten .

Im vorliegenden Fall scheint es dem Ratgeber unmöglich zu sein, ansi-termnur einen Puffernamen zu übergeben, da es nicht möglich ist, eine Liste zu erstellen, die einen Wert an Position 1 hat, aber nichts, nicht einmal nilan Position 0. Im allgemeinen Fall Es erscheint dem Ratgeber unmöglich, Argumente über Position 0 hinaus willkürlich zu ändern.

Dies scheint insofern bedauerlich, als es zum Erzeugen eines ähnlichen Effekts notwendig ist, Code zu kopieren und einzufügen: Konkret kann ich entweder die ansi-terminteraktive Form kopieren und nach meinem Geschmack erweitern, oder ich kann sie ansi-terminsgesamt kopieren und ebenfalls erweitern. In beiden Fällen muss ich jetzt einen Teil der Emacs-Lisp-Distribution in meiner Init-Datei neu definieren, was mir sowohl hinsichtlich der Haltbarkeit als auch der Ästhetik als unerwünscht erscheint.

Meine Frage ist also: Kann man mit einer solchen Liste von Argumenten fertig werden nadvice.el? Wenn das so ist, wie?

Aaron Miller
quelle
3
Warum definieren Sie nicht zusätzlich zu ansi-term Ihren eigenen interaktiven Befehl? Ich denke, das ist hier die bevorzugte Lösung.
Lunaryorn
1
Natürlich hindert mich nichts daran, aber es würde notwendig sein, den größten Teil des Muskelgedächtnisses eines Jahrzehnts zu ersetzen, das ich gerne vermeiden würde, wenn ich kann.
Aaron Miller

Antworten:

5

Dies scheint insofern bedauerlich, als es zum Erzeugen eines ähnlichen Effekts erforderlich ist, Code zu kopieren und einzufügen: [...] Ich kann ansi-termdas interaktive Formular kopieren

Im Gegenteil, ich halte es für eine gute Idee, die interaktive Form der empfohlenen Funktion zu kopieren und einzufügen, obwohl Sie dies hier eigentlich nicht tun müssen.

Ich habe deine Frage von oben bis unten gelesen. Als ich zum Code-Block kam, vermutete ich , dass Ihr Rat wahrscheinlich den Puffernamen ändert. Aber ich wusste es nicht , bis Sie später die Unterschrift als Kommentar zur Verfügung stellten.

Im vorliegenden Fall scheint es dem Ratgeber unmöglich zu sein, ansi-termnur einen Puffernamen zu übergeben, da es nicht möglich ist, eine Liste zu erstellen, die an Position 1 einen Wert hat, an Position 0 jedoch nicht einmal einen Wert nil.

In der Tat ist nichts weniger als nichts. :-) Aber das ist hier kaum relevant.

Wie Sie in der von Ihnen zitierten Dokumentation sehen können, wird der vom Hinweis zurückgegebene Wert als Argument für die empfohlene Funktion verwendet. Der Rückgabewert muss eine Liste aller Argumente sein, nicht nur derjenigen, die geändert wurden.

Bleiben Sie so nah wie möglich am alten Rat, und verwenden Sie dabei Folgendes 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)

Aber ich empfehle Ihnen, den Rat wie folgt zu definieren:

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

Diese Variante ist eigentlich selbsterklärend.

Tarsius
quelle
Für die 1. Variante müssen Sie die argsListe im Falle eines Aufrufs wie erweitern (ansi-term "foo"), sonst (setf (nth 1 args)...würde ein Fehler ausgelöst.
Npostavs
Ja, du hast Recht. Ein weiterer Grund für die Verwendung der zweiten Variante - die erste hat einen Fehler ;-) Nehmen wir zu Demonstrationszwecken einfach an, dass dies buffer-nameobligatorisch ist.
Tarsius
"Im Gegenteil, ich halte es für eine gute Idee, die interaktive Form der empfohlenen Funktion zu kopieren und einzufügen" - warum? Das Kopieren und Einfügen von Code ist in fast allen anderen Fällen eine schlechte Idee. warum nicht hier?
Aaron Miller
Eigentlich glaube ich nicht, dass "Kopieren-Einfügen" der richtige Begriff ist, ich habe es nur verwendet, weil Sie es getan haben. Aber selbst wenn es angebracht wäre, diesen Begriff hier zu verwenden, ist "Nicht kopieren-einfügen" nur eine Heuristik, keine absolute Regel. Andere Heuristiken, von denen ich denke , dass sie hier zutreffen, sind "Geben Sie Variablen und Argumenten sinnvolle Namen" und "Wenn Sie die Wahl haben, etwas zu komplizieren oder wortreich zu sein, entscheiden Sie sich für wortreich".
Tarsius
1
Ähm, eigentlich ist dies immer noch fehlerhaft, der :filter-argsHinweis erhält ein einziges Argument, das eine Liste von Argumenten für die empfohlene Funktion darstellt. Daher sollte die erste Variante wegfallen &restund die zweite Variante müsste eine Art destrukturierendes Konstrukt verwenden, um schöne Namen zu erhalten.
Npostavs
3

So würde ich es machen:

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

während ich derjenige war, der :filter-argsmich vorstellte, finde ich es persönlich selten bequem.

Stefan
quelle