9

I hate the way that elisp (not sure if LISP in general) handles multiline docstrings.

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

I sure do wish that I could do something like

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

so that the indentation was consistent.

Unfortunately, eval-when-compile does not do the job.

Does anyone have any ideas?

  • It should be fairly easy to create a macro that will expand into a defun. The drawback to that approach – and it is a big one – is that will confuse any software (other than the elisp compiler/interpreter) that is parsing your code looking for defuns. – Harald Hanche-Olsen Oct 31 '14 at 8:49
  • 3
    Funnily enough, the reason why your trick doesn't work is that eval-when-compile quotes its result (to turn it from a value to an expression). If it were a bit more clever and only quoted its result when it's not self-quoting, it would work. – Stefan Oct 31 '14 at 13:37
7

Of course a my-defun macro is the easy way out. But a simpler solution would be

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

Which should make your trick work, at least in all the cases where the function is macroexpanded before it's actually defined, which should include the main use cases (e.g. if it'sloaded from a file, if it's byte-compiled, or if it's defined via M-C-x).

Still, this won't fix all the existing code, so maybe a better answer is something like:

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

which should just shift the docstrings by 2 spaces, but only on the display side, without affecting the buffer's actual content.

  • 1
    I really like your second solution. But my irrational fear of advices makes me hinge at the first. :-) – Malabarba Oct 31 '14 at 14:32
6

You could use a macro like this:

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

Then you can define your functions like this:

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

Still, I'd strongly recommend not going against the standards for such a marginal benefit. The “irregular indentation” that bothers you is just by 2 columns, not to mention it helps highlight the first line of documentation which is more important.

  • Actually, the body of a defun is evaluated (when the function is called) and it is macro-expanded when the function is defined. So his trick should/could work. – Stefan Oct 31 '14 at 13:39
  • @Stefan That's true. Forgot eval-when-compile was a macro. – Malabarba Oct 31 '14 at 14:04
-1

I've seen packages that define docstrings like this:

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

Placing the first quote on the first line then starting the text on the next so they all line up. It's definitely not the standard but you wouldn't be the only one doing it.

  • 1
    That's a bad idea. In contexts such as Apropos, only the first line of the docstring is shown, so that first line should provide information (and stand on its own). This way you get an empty description. – Gilles Nov 10 '14 at 0:56

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.