有没有更好的方法来处理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。这种方法的缺点-很大-会使正在解析您的代码以寻找defuns的任何软件(除了elisp编译器/解释器之外)混淆。
哈拉德·汉彻·奥尔森

3
有趣的是,您的把戏不起作用的原因是eval-when-compile引用了它的结果(将其从值转换为表达式)。如果它更聪明,并且仅在不自我引用时才引用结果,那么它将起作用。
Stefan

Answers:


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
我真的很喜欢您的第二个解决方案。但是我对建议的非理性恐惧,使我一开始陷入困境。:-)
马拉巴巴2014年

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定义的主体评价(当调用该函数时),它是宏扩展定义函数时。所以他的把戏应该/可能起作用。
Stefan

@Stefan是的。忘了eval-when-compile是一个宏。
马拉巴巴2014年

-1

我见过定义如下文档字符串的软件包:

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

将第一引号放在第一行,然后在下一行开始文本,以便它们全部对齐。这绝对不是标准,但您不是唯一一个这样做的人。


1
那是个坏主意。在诸如Apropos之类的上下文中,仅显示docstring的第一行,因此第一行应提供信息(并独立存在)。这样,您得到一个空的描述。
吉尔(Gilles)'所以
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.