Чи є кращий спосіб обробляти багаторядкові доктрини в elisp?


9

Я ненавиджу те, що elisp (не впевнений, чи LISP взагалі) обробляє багаторядкові docstrings.

(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 не справляється із цією справою.

Хтось має ідеї?


Створити макрос, який перетвориться на a, має бути досить простим defun. Недолік цього підходу - і він великий - полягає в тому, що заплутає будь-яке програмне забезпечення (крім компілятора / інтерпретатора elisp), що аналізує ваш код, шукаючи defuns.
Харальд Ханш-Олсен

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
Мені дуже подобається ваше друге рішення. Але мій ірраціональний страх порад змушує мене спочатку зависати. :-)
Малабарба

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

Все-таки я настійно рекомендую не суперечити стандартам такої граничної вигоди. "Нерегулярне відступ", яке вас турбує, - це лише дві колонки, не кажучи вже про це, допомагає виділити перший рядок документації, який важливіший.


На насправді, тіло DEFUN буде оцінена (коли функція викликається) , і це макро-розширена , коли функція визначена. Тож його хитрість повинна / могла спрацювати.
Стефан

@Stefan Це правда. Забутий eval-when-compileбув макрос.
Малабарба

-1

Я бачив пакети, які визначають такі docstrings:

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

Розміщуючи першу цитату на першому рядку, потім починаючи текст у наступному, щоб вони всі вишикувалися. Це напевно не стандарт, але ти не був би єдиним, хто це робить.


1
Це погана ідея. У таких контекстах, як Apropos, відображається лише перший рядок docstring, так що перший рядок повинен надавати інформацію (і стояти самостійно). Таким чином ви отримуєте порожній опис.
Жил 'SO- перестань бути злим'
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.