Làm thế nào để thao tác danh sách đối số trong nadvice.el?


12

Tiếp theo từ một câu trả lời cho một câu hỏi khác về hệ thống tư vấn mới :

Theo kiểu cũ advice.el, có thể thao túng các thành viên riêng lẻ trong danh sách đối số của hàm được khuyên, mà không đưa ra bất kỳ xác nhận nào về các thành viên đó không bị thao túng. Ví dụ: lời khuyên sau:

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

cho phép cung cấp (tùy chọn) đối số tên đệm cho ansi-termcuộc gọi, trong khi ansi-termvẫn sẽ có được đối số đầu tiên bằng cách nhắc theo hình thức tương tác của chính nó.

(Để tham khảo sau này, ansi-termchữ ký (PROGRAM &optional BUFFER-NAME)của nó là và biểu mẫu tương tác của nó nhắc PROGRAM với một số giá trị mặc định có thể có, nhưng không có gì liên quan đến BUFFER-NAME.)

Tôi không chắc liệu điều này có khả thi hay không nadvice.el. Nếu có, tôi không chắc nó có thể được thực hiện như thế nào. Tôi đã tìm thấy một vài cách để thay thế danh sách đối số của hàm được khuyên.

Ví dụ: từ * thông tin * (elisp) Bộ kết hợp lời khuyên :

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

Các tổ hợp khác cung cấp các khả năng tương tự và chủ đề chung trong số đó là, trong khi danh sách đối số của hàm có thể được thay thế, cắt ngắn, mở rộng, et al, không có cách nào rõ ràng để tư vấn chức năng sửa đổi đối số tại một vị trí nhất định trong danh sách mà không có khẳng định bất cứ điều gì về phần còn lại của nó .

Trong trường hợp đang thảo luận, tác giả lời khuyên ansi-termchỉ có thể chuyển tên đệm, vì không thể tạo danh sách có giá trị ở vị trí 1 nhưng không có gì, thậm chí không nilở vị trí 0. Trong trường hợp chung, tác giả lời khuyên dường như không thể tự ý sửa đổi các đối số ngoài vị trí 0.

Điều này có vẻ đáng tiếc ở chỗ, để tạo ra hiệu ứng tương tự, cần phải sao chép-dán mã: cụ thể, tôi có thể sao chép ansi-termhình thức tương tác của mình và mở rộng nó theo sở thích của mình, hoặc tôi có thể sao chép ansi-termhoàn toàn và mở rộng tương tự. Trong cả hai trường hợp, bây giờ tôi phải xác định lại một phần của bản phân phối Emacs Lisp trong tệp init của tôi, điều này khiến tôi không mong muốn về cả độ bền và tính thẩm mỹ.

Sau đó, câu hỏi của tôi là: Loại danh sách đối số này có thể được thực hiện với nadvice.el? Nếu vậy thì thế nào?


3
Tại sao bạn không xác định lệnh tương tác của riêng bạn trên đầu thuật ngữ ansi? Tôi nghĩ đó là giải pháp thích hợp hơn ở đây.
lunaryorn

1
Tất nhiên không có gì ngăn cản tôi làm điều đó, nhưng nó sẽ cần phải thay thế phần tốt hơn của bộ nhớ cơ bắp trong một thập kỷ, điều mà tôi muốn tránh nếu tôi có thể.
Aaron Miller

Câu trả lời:


5

Điều này có vẻ đáng tiếc ở chỗ, để tạo ra hiệu ứng tương tự, cần phải sao chép-dán mã: [...] Tôi có thể sao chép ansi-termhình thức tương tác của

Ngược lại, tôi nghĩ sẽ là một ý tưởng tốt để sao chép-dán hình thức tương tác của chức năng được tư vấn, mặc dù bạn không thực sự phải làm như vậy ở đây.

Tôi đọc bạn câu hỏi từ trên xuống dưới. Khi tôi đến khối mã, tôi đoán rằng lời khuyên của bạn có lẽ là thay đổi tên bộ đệm. Nhưng tôi không biết cho đến khi bạn cung cấp chữ ký như một bình luận.

Trong trường hợp đang thảo luận, tác giả lời khuyên dường như không thể ansi-termchỉ chuyển một tên bộ đệm, vì không thể tạo danh sách có giá trị ở vị trí 1 nhưng không có gì, thậm chí không nilở vị trí 0.

Quả thực không có gì là ít hơn không có gì. :-) Nhưng điều đó hầu như không liên quan ở đây.

Như bạn có thể thấy trong tài liệu bạn đã trích dẫn, giá trị được trả về bởi lời khuyên được sử dụng làm đối số cho hàm được tư vấn. Giá trị trả về phải là một danh sách tất cả các đối số không chỉ các đối số đã thay đổi.

Ở gần nhất có thể với lời khuyên cũ, đây là những gì bạn sẽ phải làm bằng cách sử dụng 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)

Nhưng tôi khuyên bạn nên xác định lời khuyên như thế này thay vào đó:

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

Biến thể này thực sự là tự giải thích.


Đối với biến thể thứ 1, bạn cần mở rộng argsdanh sách trong trường hợp có cuộc gọi như thế (ansi-term "foo"), nếu không (setf (nth 1 args)...sẽ gây ra lỗi.
npostavs

Vâng bạn đã đúng. Một lý do khác để sử dụng biến thể thứ hai - biến thể thứ nhất có lỗi ;-) Cho phép, với mục đích trình diễn, chỉ cần cho rằng đó buffer-namelà bắt buộc.
tarsius

"Ngược lại, tôi nghĩ sẽ là một ý tưởng tốt để sao chép-dán hình thức tương tác của chức năng được tư vấn" - tại sao vậy? Mã sao chép dán là một ý tưởng tồi trong hầu hết các trường hợp khác; tại sao không ở đây?
Aaron Miller

Thật ra tôi không nghĩ "copy-paste" là thuật ngữ đúng trong trường hợp này, tôi chỉ sử dụng nó vì bạn đã làm. Nhưng ngay cả khi nó phù hợp để sử dụng thuật ngữ đó ở đây, thì "không sao chép-dán" chỉ là một heuristic không phải là một quy tắc tuyệt đối. Chẩn đoán khác, mà tôi nghĩ làm áp dụng ở đây, là "cho tên có ý nghĩa cho các biến và lập luận" và "khi bạn có một sự lựa chọn giữa làm phức tạp một cái gì đó hoặc bị tiết, đi với verbose".
tarsius

1
Ừm, thực ra, điều này vẫn còn bị hỏng, :filter-argslời khuyên nhận được một đối số duy nhất là danh sách các đối số cho hàm được khuyên, vì vậy biến thể thứ 1 sẽ bỏ &restvà biến thể thứ 2 sẽ phải sử dụng một số cấu trúc phá hủy để có được tên đẹp.
npostavs

3

Đây là cách tôi là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)

trong khi tôi là người giới thiệu :filter-argscá nhân tôi thấy nó hiếm khi thuận tiện.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.