วิธีจัดการรายการอาร์กิวเมนต์ใน nadvice.el


12

ทำตามคำตอบของคำถามอีกข้อหนึ่งเกี่ยวกับระบบคำแนะนำใหม่ :

ในแบบเก่าadvice.elมันเป็นไปได้ที่จะจัดการกับสมาชิกแต่ละคนของรายการอาร์กิวเมนต์ของฟังก์ชั่นที่แนะนำโดยไม่ต้องทำการยืนยันใด ๆ เกี่ยวกับสมาชิกเหล่านั้นที่ไม่ได้จัดการ ตัวอย่างเช่นคำแนะนำต่อไปนี้:

(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-termในขณะที่ansi-termจะยังคงได้รับอาร์กิวเมนต์แรกโดยการพร้อมท์ตามรูปแบบโต้ตอบของตัวเอง

(สำหรับการอ้างอิงในภายหลังansi-termลายเซ็นของคือ(PROGRAM &optional BUFFER-NAME)และรูปแบบการโต้ตอบของมันแจ้งให้ PROGRAM ที่มีค่าเริ่มต้นที่เป็นไปได้หลายประการ แต่ไม่ได้ทำอะไรเกี่ยวกับ BUFFER-NAME)

nadvice.elฉันไม่แน่ใจหรือไม่ว่านี้คือในความเป็นไปได้ หากเป็นเช่นนั้นฉันไม่แน่ใจว่าจะสามารถทำได้ ฉันพบสองวิธีในการแทนที่รายการอาร์กิวเมนต์ของฟังก์ชันที่แนะนำ

ตัวอย่างเช่นจากcombinators คำแนะนำจาก* info * (elisp) :

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

combinators อื่น ๆ ให้ความสามารถที่คล้ายกันและด้ายที่พบบ่อยในหมู่พวกเขาคือว่าในขณะที่รายการอาร์กิวเมนต์ของฟังก์ชันอาจถูกแทนที่ตัดทอนขยาย, et al, ไม่มีวิธีที่ชัดเจนสำหรับคำแนะนำฟังก์ชั่นในการปรับเปลี่ยนการโต้แย้งในตำแหน่งที่ระบุในรายการโดยไม่ต้อง เข้าไปยุ่งอะไรเกี่ยวกับส่วนที่เหลือของมัน

ในกรณีภายใต้การสนทนาดูเหมือนว่าเป็นไปไม่ได้ที่ผู้สร้างคำแนะนำจะส่งผ่านansi-termชื่อบัฟเฟอร์เท่านั้นเนื่องจากไม่สามารถสร้างรายการที่มีค่าในตำแหน่งที่ 1 แต่ไม่มีอะไรเลยแม้แต่nilในตำแหน่งที่ 0 ในกรณีทั่วไป มันเป็นไปไม่ได้สำหรับผู้เขียนคำแนะนำในการแก้ไขข้อโต้แย้งโดยพลการเกินกว่าตำแหน่ง 0

ดูเหมือนว่าโชคร้ายในการที่จะสร้างเอฟเฟ็กต์ที่คล้ายกันก็จำเป็นต้องคัดลอกโค้ด: โดยเฉพาะอย่างยิ่งฉันสามารถคัดลอกansi-termรูปแบบการโต้ตอบและขยายไปสู่รสนิยมของฉันหรือฉันสามารถคัดลอกansi-termทั้งหมดและขยายได้เช่นกัน ไม่ว่าในกรณีใดตอนนี้ฉันต้องกำหนดส่วนหนึ่งของการกระจาย Emacs Lisp ในไฟล์ init ซึ่งทำให้ฉันไม่พึงประสงค์ในแง่ของความทนทานและความสวยงาม

จากนั้นคำถามของฉันคือ: รายการอาร์กิวเมนต์ประเภทนี้สามารถสร้างด้วยอาร์กิวเมนต์ได้nadvice.elหรือไม่? ถ้าเป็นเช่นนั้นได้อย่างไร


3
ทำไมคุณไม่กำหนดคำสั่งเชิงโต้ตอบของคุณเองให้เป็นคำศัพท์ ฉันคิดว่านั่นเป็นทางออกที่ดีกว่าที่นี่
Lunaryorn

1
แน่นอนว่าไม่มีอะไรหยุดยั้งฉันจากการทำเช่นนั้นได้ แต่มันจำเป็นที่จะต้องเปลี่ยนส่วนที่ดีขึ้นของความทรงจำของกล้ามเนื้อในทศวรรษที่ผ่านมาซึ่งฉันต้องการหลีกเลี่ยงถ้าทำได้
Aaron Miller

คำตอบ:


5

ดูเหมือนว่าจะโชคร้ายในการสร้างเอฟเฟ็กต์ที่คล้ายกันก็จำเป็นต้องคัดลอกรหัส: [... ] ฉันสามารถคัดลอกansi-termแบบฟอร์มการโต้ตอบของ

ในทางตรงกันข้ามฉันคิดว่ามันเป็นความคิดที่ดีที่จะคัดลอกรูปแบบอินเทอร์แอคทีฟของฟังก์ชั่นที่แนะนำแม้ว่าคุณไม่จำเป็นต้องทำที่นี่

ฉันอ่านคำถามจากบนลงล่าง เมื่อฉันไปถึงรหัสบล็อกฉันเดาว่าคำแนะนำของคุณอาจเปลี่ยนชื่อบัฟเฟอร์ แต่ฉันไม่ทราบจนกว่าคุณจะให้ความคิดเห็นในภายหลัง

ในกรณีภายใต้การสนทนาดูเหมือนว่าเป็นไปไม่ได้ที่ผู้สร้างคำแนะนำจะส่งผ่านansi-termชื่อบัฟเฟอร์เท่านั้นเนื่องจากไม่สามารถสร้างรายการที่มีค่าในตำแหน่ง 1 แต่ไม่มีอะไรเลยแม้แต่nilในตำแหน่ง 0

แน่นอนไม่มีอะไรน้อยกว่าไม่มีอะไร :-) แต่มันไม่ค่อยเกี่ยวข้องกันที่นี่

ดังที่คุณเห็นในเอกสารที่คุณยกมาค่าที่ส่งคืนโดยคำแนะนำจะถูกใช้เป็นอาร์กิวเมนต์ของฟังก์ชันที่แนะนำ ค่าส่งคืนจะต้องเป็นรายการของอาร์กิวเมนต์ทั้งหมดไม่ใช่แค่ค่าที่มีการเปลี่ยนแปลง

อยู่ใกล้ที่สุดเท่าที่จะทำได้เพื่อให้คำแนะนำเก่านี่คือสิ่งที่คุณจะต้องใช้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)

แต่ฉันขอแนะนำให้คุณนิยามคำแนะนำเช่นนี้แทน:

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

ตัวแปรนี้อธิบายได้ด้วยตนเอง


สำหรับตัวแปรที่ 1 คุณต้องขยายargsรายการในกรณีที่มีการโทรเช่น(ansi-term "foo")นั้นมิฉะนั้น(setf (nth 1 args)...จะเกิดข้อผิดพลาด
npostavs

ใช่คุณพูดถูก. อีกเหตุผลหนึ่งที่ใช้ตัวแปรตัวที่สอง - ตัวแรกมีบั๊ก ;-) ให้สำหรับการสาธิตเพียงแค่สมมติว่าbuffer-nameเป็นข้อบังคับ
Tarsius

"ในทางตรงกันข้ามฉันคิดว่ามันเป็นความคิดที่ดีที่จะคัดลอกรูปแบบการโต้ตอบของฟังก์ชั่นที่แนะนำ" - ทำไมเหรอ? รหัสการคัดลอกวางเป็นความคิดที่ดีในทุกกรณี ทำไมไม่มาที่นี่?
แอรอนมิลเลอร์

จริงๆแล้วฉันไม่คิดว่า "copy-paste" เป็นคำที่ถูกต้องในกรณีนี้ฉันเพิ่งใช้เพราะคุณทำ แต่แม้ว่ามันจะเหมาะสมที่จะใช้คำนั้นที่นี่แล้ว "อย่าคัดลอก - วาง" เป็นเพียงการเรียนรู้ไม่ใช่กฎที่แน่นอน การวิเคราะห์พฤติกรรมอื่น ๆ ซึ่งผมคิดว่าสิ่งที่ต้องทำใช้ที่นี่เป็น "ให้ชื่อมีความหมายกับตัวแปรและการขัดแย้ง" และ "เมื่อคุณมีทางเลือกระหว่างแทรกซ้อนบางสิ่งบางอย่างหรือเป็น verbose ไปด้วยอย่างละเอียด"
Tarsius

1
อืมจริงๆแล้วมันยังพังอยู่:filter-argsคำแนะนำจะได้รับอาร์กิวเมนต์เดี่ยวซึ่งเป็นรายการของ args ไปยังฟังก์ชันที่แนะนำดังนั้นตัวแปรที่ 1 ควรจะลดลง&restและตัวแปรที่สองจะต้องใช้โครงสร้างการทำลายล้างเพื่อให้ได้ชื่อที่ดี
npostavs

3

นี่คือวิธีที่ฉันจะทำ:

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

ในขณะที่ฉันเป็นคนที่แนะนำตัว:filter-argsฉันพบว่ามันไม่ค่อยสะดวก

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.