elisp에서 여러 줄 docstring을 처리하는 더 좋은 방법이 있습니까?


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. 이 접근법의 단점은 큰 문제 defun입니다. 코드를 파싱하는 모든 소프트웨어 (elisp 컴파일러 / 인터프리터 제외)를 혼동 할 것 입니다.
Harald Hanche-Olsen

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
나는 당신의 두 번째 솔루션을 정말로 좋아합니다. 그러나 충고에 대한 나의 비이성적 인 두려움은 처음에 힌지를 만든다. :-)
Malabarba

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

-1

다음과 같이 docstring을 정의하는 패키지를 보았습니다.

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

첫 번째 따옴표를 첫 번째 줄에 배치하고 다음 줄에서 텍스트를 시작하여 모두 정렬하십시오. 확실히 표준은 아니지만 당신이 그것을하는 유일한 사람은 아닙니다.


1
나쁜 생각입니다. Apropos와 같은 컨텍스트에서는 문서 문자열의 첫 번째 행만 표시되므로 첫 번째 행은 정보를 제공해야하며 자체적으로 있어야합니다. 이 방법으로 빈 설명을 얻을 수 있습니다.
Gilles 'SO- 악의를 그만두십시오
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.