Gibt es eine bessere Möglichkeit, mit mehrzeiligen Dokumentzeichenfolgen in elisp umzugehen?

9

Ich hasse die Art und Weise, wie elisp (nicht sicher, ob LISP im Allgemeinen) mit mehrzeiligen Dokumentzeichenfolgen umgeht.

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

Ich wünschte wirklich, ich könnte so etwas tun

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

so dass die Einrückung konsistent war.

Leider macht eval-when-compile den Job nicht.

Hat jemand irgendwelche Ideen?

Krazy Glew
quelle
Es sollte ziemlich einfach sein, ein Makro zu erstellen, das sich zu einem Makro erweitert defun. Der Nachteil dieses Ansatzes - und es ist ein großer - ist, dass jede Software (außer dem Elisp-Compiler / Interpreter) verwirrt wird, die Ihren Code analysiert und nach defuns sucht .
Harald Hanche-Olsen
3
Lustigerweise ist der Grund, warum Ihr Trick nicht funktioniert, dass er eval-when-compiledas Ergebnis zitiert (um ihn von einem Wert in einen Ausdruck umzuwandeln ). Wenn es ein bisschen schlauer wäre und sein Ergebnis nur dann zitieren würde, wenn es nicht selbst zitiert, würde es funktionieren.
Stefan

Antworten:

7

Natürlich ist ein my-defunMakro der einfache Ausweg. Aber eine einfachere Lösung wäre

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

Damit sollte Ihr Trick funktionieren, zumindest in allen Fällen, in denen die Funktion vor ihrer tatsächlichen Definition makroerweitert ist, einschließlich der wichtigsten Anwendungsfälle (z. B. wenn sie aus einer Datei geladen wird, wenn sie bytekompiliert ist oder wenn sie definiert ist via M-C-x).

Dies behebt jedoch nicht den gesamten vorhandenen Code. Vielleicht ist eine bessere Antwort so etwas wie:

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

Dies sollte nur die Dokumentzeichenfolgen um 2 Leerzeichen verschieben, jedoch nur auf der Anzeigeseite, ohne den tatsächlichen Inhalt des Puffers zu beeinflussen.

Stefan
quelle
1
Ihre zweite Lösung gefällt mir sehr gut. Aber meine irrationale Angst vor Ratschlägen lässt mich zuerst hängen. :-)
Malabarba
6

Sie könnten ein Makro wie das folgende verwenden:

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

Dann können Sie Ihre Funktionen folgendermaßen definieren:

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

Trotzdem würde ich dringend empfehlen, nicht gegen die Standards zu verstoßen, um einen so geringen Nutzen zu erzielen. Die „unregelmäßige Einrückung“, die Sie stört, besteht nur aus zwei Spalten, ganz zu schweigen davon, dass sie die erste wichtige Dokumentationszeile hervorhebt.

Malabarba
quelle
Eigentlich ist der Körper eines defun wird ausgewertet (wenn die Funktion aufgerufen wird) und es ist Makro erweitert , wenn die Funktion definiert ist. Also sollte / könnte sein Trick funktionieren.
Stefan
@Stefan Das stimmt. Vergessen eval-when-compilewar ein Makro.
Malabarba
-1

Ich habe Pakete gesehen, die Docstrings wie folgt definieren:

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

Platzieren Sie das erste Zitat in der ersten Zeile und beginnen Sie den Text in der nächsten, damit alle in einer Reihe stehen. Es ist definitiv nicht der Standard, aber Sie wären nicht der einzige, der es tut.

Jordon Biondo
quelle
1
Das ist eine schlechte Idee. In Kontexten wie Apropos wird nur die erste Zeile der Dokumentzeichenfolge angezeigt, sodass die erste Zeile Informationen bereitstellen sollte (und für sich allein stehen sollte). Auf diese Weise erhalten Sie eine leere Beschreibung.
Gilles 'SO - hör auf böse zu sein'