elispで複数行のドキュメント文字列を処理するより良い方法はありますか?


9

elisp(一般的にLISPかどうかは不明)が複数行のdocstringを処理する方法が嫌いです。

(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コンパイラ/インタプリタ以外の)ソフトウェアを混乱させることです。
Harald Hanche-Olsen 2014

3
おかしなことに、トリックが機能しない理由は、eval-when-compileその結果を引用することです(値から式に変換するため)。それがもう少し賢く、自己引用ではない場合にのみ結果を引用すると、うまくいきます。
Stefan

回答:


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

これは、docstringを2スペースだけシフトする必要がありますが、バッファの実際のコンテンツに影響を与えることなく、表示側のみです。


1
私はあなたの2番目のソリューションが本当に好きです。しかし、アドバイスに対する私の不合理な恐怖は、私を最初に左右します。:-)
マラバルバ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たのはマクロだった。
Malabarba 2014年

-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-邪悪なことをやめなさい」2014
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.