Esiste un modo migliore per gestire le dotstring multilinea in elisp?


9

Odio il modo in cui elisp (non sono sicuro che LISP in generale) gestisca dotstring multilinea.

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

Sicuramente vorrei poter fare qualcosa del genere

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

in modo che il rientro fosse coerente.

Sfortunatamente, eval-when-compile non fa il lavoro.

Qualcuno ha qualche idea?


Dovrebbe essere abbastanza facile creare una macro che si espanderà in a defun. Lo svantaggio di questo approccio - ed è grande - è che confonderà qualsiasi software (diverso dal compilatore / interprete elisp) che sta analizzando il tuo codice alla ricerca di defuns.
Harald Hanche-Olsen,

3
Stranamente, il motivo per cui il tuo trucco non funziona è che ne eval-when-compilecita il risultato (per trasformarlo da un valore in un'espressione). Se fosse un po 'più intelligente e citasse il suo risultato solo quando non è auto-quotato, funzionerebbe.
Stefan,

Risposte:


7

Naturalmente una my-defunmacro è la via d'uscita facile. Ma sarebbe una soluzione più semplice

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

Il che dovrebbe far funzionare il tuo trucco, almeno in tutti i casi in cui la funzione è macroespanduta prima che sia effettivamente definita, che dovrebbe includere i principali casi d'uso (ad esempio se è caricato da un file, se è compilato in byte o se è definito via M-C-x).

Tuttavia, questo non risolverà tutto il codice esistente, quindi forse una risposta migliore è qualcosa del tipo:

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

che dovrebbe semplicemente spostare i docstringi di 2 spazi, ma solo sul lato del display, senza influire sul contenuto effettivo del buffer.


1
Mi piace molto la tua seconda soluzione. Ma la mia irrazionale paura dei consigli mi fa perno all'inizio. :-)
Malabarba,

6

È possibile utilizzare una macro come questa:

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

Quindi puoi definire le tue funzioni in questo modo:

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

Tuttavia, consiglio vivamente di non andare contro gli standard per un vantaggio così marginale. La "rientranza irregolare" che ti disturba è solo di 2 colonne, per non parlare del fatto che aiuta a evidenziare la prima riga della documentazione che è più importante.


In realtà, il corpo di un defun viene valutato (quando viene chiamata la funzione) ed è macro-espanso quando viene definita la funzione. Quindi il suo trucco dovrebbe / potrebbe funzionare.
Stefan,

@Stefan È vero. Dimenticato eval-when-compileera una macro.
Malabarba,

-1

Ho visto pacchetti che definiscono dotstring come questo:

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

Posizionando la prima virgoletta sulla prima riga, quindi iniziare il testo sulla successiva in modo che si allineino. Non è sicuramente lo standard ma non saresti l'unico a farlo.


1
Questa è una cattiva idea. In contesti come Apropos, viene mostrata solo la prima riga del docstring, in modo che la prima riga dovrebbe fornire informazioni (e stare da sola). In questo modo si ottiene una descrizione vuota.
Gilles 'SO- smetti di essere malvagio' il
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.