Есть ли лучший способ обработки многострочных строк документации в 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.
Харальд Ханче-Олсен
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))

Тем не менее, я настоятельно рекомендую не идти против стандартов для такой предельной выгоды. «Нерегулярный отступ», который беспокоит вас, - это всего лишь 2 столбца, не говоря уже о том, что он помогает выделить первую строку документации, которая является более важной.

Malabarba
источник
На самом деле, тело DEFUN будет оценена (когда функция вызывается) , и это макро-расширена , когда функция определена. Так что его трюк должен / мог сработать.
Стефан,
@ Stefan Это правда. Забыл eval-when-compileбыл макрос.
Малабарба
-1

Я видел пакеты, которые определяют строки документации, как это:

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

Поместите первую цитату в первую строку, затем начните текст в следующей строке, чтобы они все выстроились в линию. Это определенно не стандарт, но вы не единственный, кто это делает.

Джордон Биондо
источник
1
Это плохая идея. В таких контекстах, как Apropos, отображается только первая строка строки документа, поэтому первая строка должна предоставлять информацию (и стоять самостоятельно). Таким образом, вы получите пустое описание.
Жиль "ТАК - перестань быть злым"