มีวิธีที่ดีกว่าในการจัดการเอกสารหลายบรรทัดด้วย elisp หรือไม่?


9

ฉันเกลียดวิธีที่ elisp (ไม่แน่ใจว่า LISP โดยทั่วไป) จัดการเอกสารหลายบรรทัด

(defun foo ()
  "This is
a multi
liner
docstring"
  (do-stuff))

ฉันแน่ใจว่าหวังว่าฉันจะทำสิ่งที่ชอบ

(defun foo ()
  (eval-when-compile 
    (concat
      "This is\n"
       "a multi\n"
       "line\n"
       "docstring"))
  (do-stuff))

เพื่อให้การเยื้องสอดคล้องกัน

น่าเสียดายที่คอมไพล์ eval-when-compile ไม่สามารถทำงานได้

ไม่มีใครมีความคิดใด ๆ


defunมันควรจะค่อนข้างง่ายที่จะสร้างแมโครที่จะขยายไปที่ ข้อเสียเปรียบที่จะวิธีการที่ - และมันเป็นขนาดใหญ่ - มีการที่จะสร้างความสับสนให้ซอฟต์แวร์ใด ๆ (นอกเหนือจาก Elisp คอมไพเลอร์ / ล่าม) ที่มีการแยกรหัสของคุณมองหาdefuns
Harald Hanche-Olsen

3
สนุกมากเหตุผลที่เคล็ดลับของคุณใช้ไม่ได้ผลก็คือeval-when-compileอ้างถึงผลลัพธ์ (เพื่อเปลี่ยนจากค่าเป็นนิพจน์) หากมันฉลาดกว่านี้เล็กน้อยและอ้างถึงผลลัพธ์เมื่อไม่ได้พูดด้วยตนเองมันจะได้ผล
สเตฟาน

คำตอบ:


7

แน่นอนว่าmy-defunแมโครเป็นวิธีที่ง่าย แต่ทางออกที่ง่ายกว่าคือ

(advice-add 'eval-when-compile :filter-return
            (lambda (exp)
              (if (and (eq 'quote (car-safe exp))
                       (stringp (cadr exp)))
                  (cadr exp)
                exp)))

ซึ่งควรทำให้เคล็ดลับของคุณทำงานอย่างน้อยที่สุดในทุกกรณีที่ฟังก์ชั่นมาโครขยายตัวก่อนที่มันจะถูกกำหนดจริง ๆ ซึ่งควรรวมถึงกรณีการใช้งานหลัก (เช่นถ้ามันถูกโหลดจากไฟล์ถ้าคอมไพล์ด้วยไบต์หรือถ้ามันถูกกำหนด ผ่านM-C-x)

ยังนี้จะไม่แก้ไขรหัสที่มีอยู่ทั้งหมดดังนั้นอาจเป็นคำตอบที่ดีกว่าคือ:

;; -*- lexical-binding:t -*-

(defun my-shift-docstrings (orig ppss)
  (let ((face (funcall orig ppss)))
    (when (eq face 'font-lock-doc-face)
      (save-excursion
        (let ((start (point)))
          (parse-partial-sexp (point) (point-max) nil nil ppss 'syntax-table)
          (while (search-backward "\n" start t)
            (put-text-property (point) (1+ (point)) 'display
                               (propertize "\n  " 'cursor 0))))))
    face))

(add-hook 'emacs-lisp-mode-hook
          (lambda ()
            (font-lock-mode 1)
            (push 'display font-lock-extra-managed-props)
            (add-function :around (local 'font-lock-syntactic-face-function)
                          #'my-shift-docstrings)))

ซึ่งควรเปลี่ยนเอกสารโดย 2 ช่องว่าง แต่เฉพาะในด้านการแสดงผลโดยไม่มีผลกับเนื้อหาจริงของบัฟเฟอร์


1
ฉันชอบโซลูชันที่สองของคุณจริงๆ แต่ความกลัวที่ไม่มีเหตุผลของฉันของคำแนะนำทำให้ฉันบานพับในตอนแรก :-)
Malabarba

6

คุณสามารถใช้มาโครแบบนี้:

(defmacro my-defun (name arglist &rest forms)
  "Like `defun', but concatenates strings."
  (declare (indent defun))
  (let (doc-lines)
    (while (and (stringp (car-safe forms))
                (> (length forms) 1))
      (setq doc-lines
            (append doc-lines (list (pop forms)))))
    `(defun ,name ,arglist
       ,(mapconcat #'identity doc-lines "\n")
       ,@forms)))

จากนั้นคุณสามารถกำหนดฟังก์ชั่นของคุณดังนี้:

(my-defun test (a)
  "Description"
  "asodksad"
  "ok"
  (interactive)
  (+ 1 a))

ถึงกระนั้นฉันขอแนะนำไม่ให้ขัดกับมาตรฐานเพื่อผลประโยชน์ส่วนเพิ่ม "การเยื้องที่ผิดปกติ" ที่รบกวนคุณเพียง 2 คอลัมน์ไม่ต้องพูดถึงมันจะช่วยเน้นบรรทัดแรกของเอกสารซึ่งมีความสำคัญมากกว่า


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

@tefan นั่นเป็นเรื่องจริง ลืมeval-when-compileเป็นมาโคร
Malabarba

-1

ฉันเคยเห็นแพ็คเกจที่กำหนด docstrings เช่นนี้:

(defun my-function (x y) "
this is my docstring
that lines always lines up
across multiple lines."
  (+ x y))

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


1
นั่นเป็นความคิดที่ไม่ดี ในบริบทเช่น Apropos จะแสดงเฉพาะบรรทัดแรกของ docstring เพื่อให้บรรทัดแรกควรให้ข้อมูล (และยืนด้วยตนเอง) วิธีนี้คุณจะได้คำอธิบายที่ว่างเปล่า
Gilles 'SO- หยุดความชั่วร้าย'
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.