Nadvice.el'de argüman listesi nasıl değiştirilir?


12

İtibaren ardından yeni danışma sistemi hakkında başka soruya bir cevap :

Eski tarzda advice.el, tavsiye edilen bir işlevin argüman listesinin bireysel üyelerini, manipüle edilmeyen bu üyelerle ilgili herhangi bir iddiada bulunmaksızın manipüle etmek mümkün oldu. Örneğin, aşağıdaki öneriler:

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

ansi-termaramaya bir arabellek adı bağımsız değişkeninin (isteğe bağlı) sağlanmasına izin verirken ansi-term, yine de ilk bağımsız değişkenini kendi etkileşimli biçimine göre sorarak elde eder.

(Daha sonra başvurmak için, ansi-termimzasıdır (PROGRAM &optional BUFFER-NAME)ve etkileşimli formu birkaç olası varsayılan ile PROGRAM ister, ancak BUFFER-NAME ile ilgili hiçbir şey yapmaz.)

Bunun mümkün olup olmadığından emin değilim nadvice.el. Eğer öyleyse, nasıl yapılabileceğinden emin değilim. Tavsiye edilen bir işlevin argüman listesini değiştirmek için birkaç yol buldum .

Örneğin, * info * (elisp) 'den Tavsiye birleştiricileri :

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

Diğer birleştiriciler benzer yetenekler sağlar ve aralarındaki ortak iş parçacığı, bir işlevin bağımsız değişken listesinin değiştirilebileceği, kesilebileceği, genişletilebileceği, ancak işlev önerisinin bağımsız değişkenleri listede belirli bir konumda değiştirmeden görünür bir yolu olmadığıdır. geri kalanı hakkında bir şey iddia etmek .

Tartışılan durumda, tavsiye yazarının ansi-termsadece bir arabellek adı geçmesi imkansız görünmektedir , çünkü konum 1'de bir değeri olan, ancak nil0 konumunda bile hiçbir şey olmayan bir liste oluşturmak mümkün değildir. tavsiye yazarının 0 konumundan ötesindeki argümanları keyfi olarak değiştirmesi imkansız görünmektedir.

Bu, benzer bir etki üretmek için kodu kopyalayıp yapıştırmak gerektiğinde talihsiz görünüyor: özellikle, ya ansi-termetkileşimli formunu kopyalayabilir ve zevkime göre genişletebilirim ya da ansi-termtamamen kopyalayıp aynı şekilde genişletebilirim. Her iki durumda da, init dosyamdaki Emacs Lisp dağıtımının bir kısmını yeniden tanımlamalıyım, bu da hem dayanıklılık hem de estetik açısından beni istenmeyen bir yere çarpıyor.

O zaman sorum şu: Bu tür bir tartışma listesi yönetimi ile yapılabilir nadvice.elmi? Öyleyse nasıl?


3
Neden kendi etkileşimli komutunuzu ansi terimi üzerinde tanımlamıyorsunuz? Bence bu tercih edilen çözüm.
lunaryorn

1
Tabii ki bunu yapmamı engelleyen hiçbir şey yok, ancak on yıllık kas belleğinin daha iyi kısmını değiştirmeyi gerektirecekti, eğer yapabilirsem kaçınmak istiyorum.
Aaron Miller

Yanıtlar:


5

Bu talihsiz görünüyor, benzer bir etki üretmek için kodu kopyalayıp yapıştırmak gerekiyor: [...] ansi-termEtkileşimli formunu kopyalayabilirim

Aksine, burada gerçekten yapmak zorunda olmasanız bile, tavsiye edilen işlevin etkileşimli formunu kopyalayıp yapıştırmanın iyi bir fikir olacağını düşünüyorum.

Soruyu yukarıdan aşağıya okudum. Kod bloğuna geldiğimde , tavsiyenizin muhtemelen arabellek adını değiştirdiğini tahmin ettim . Ama daha sonra imzayı yorum olarak verene kadar bilmiyordum .

Tartışılan durumda, tavsiye yazarının ansi-termsadece bir arabellek adı geçmesi imkansız görünmektedir , çünkü konum 1'de bir değeri olan, ancak nil0 konumunda hiçbir şey olmayan bir liste oluşturmak mümkün değildir .

Aslında hiçbir şey hiçbir şeyden daha az bir şey değildir. :-) Ama bu burada pek alakalı değil.

Alıntıladığınız belgelerde görebileceğiniz gibi, tavsiye tarafından döndürülen değer, önerilen işleve argümanlar olarak kullanılır. Dönüş değeri, yalnızca değişen argümanların değil, tüm argümanların listesi olmalıdır.

Eski tavsiyeye olabildiğince yakın kalmak, bunu kullanarak yapmanız gerekenler 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)

Ancak bunun yerine tavsiyeyi tanımlamanızı tavsiye ederim:

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

Bu varyant aslında kendini açıklamaktadır.


1. varyant için, argsbenzer bir arama durumunda listeyi genişletmeniz gerekir (ansi-term "foo"), aksi takdirde (setf (nth 1 args)...bir hata ortaya çıkarır.
npostavs

Evet haklısın. İkinci varyantı kullanmanın başka bir nedeni - ilkinin bir hatası var ;-) Gösteri amacıyla, sadece buffer-namezorunlu olduğunu varsayalım .
tarsius

"Aksine, tavsiye edilen işlevin etkileşimli formunu kopyalayıp yapıştırmanın iyi bir fikir olacağını düşünüyorum" - neden böyle? Kopya yapıştırma kodu hemen hemen her durumda kötü bir fikirdir; neden burada değil?
Aaron Miller

Aslında "kopyala-yapıştır" ın bu durumda doğru terim olduğunu düşünmüyorum, sadece kullandığınız için kullandım. Ancak bu terimi burada kullanmak uygun olsa bile, "kopyalayıp yapıştırma" mutlak bir kural değil sezgiseldir. Bence diğer buluşsal yöntem, do vardır, burada uygulamak ve "Bir şey zorlaştırıcı uygulamalar ve ayrıntılı olmak arasında bir seçim olduğunda, ayrıntılı ile gitmek" "değişkenler ve argümanlar için anlamlı isimler verin".
tarsius

1
Um, aslında, bu hala kırılmış, :filter-argstavsiye edilen fonksiyonun argümanlarının bir listesi olan tek bir argüman alır , bu nedenle 1. varyant düşmeli &restve 2. varyant güzel isimler elde etmek için bir tür yıkıcı yapı kullanmak zorunda kalacaktır.
npostavs

3

İşte böyle yapardım:

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

Ben tanıtan :filter-argsben iken ben şahsen nadiren uygun buluyorum.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.