Generate emacs-lisp documentation

| categories: emacs_lisp | tags:

Emacs has some pretty amazing features to get help on a function (describe-function), to navigate quickly to functions in an elisp file (speedbar and imenu). Other languages have tools for generating documentation for all the functions in a file, e.g. epydoc, javadoc, Doxygen,… I have not found an equivalent to this in emacs-lisp. Here, we explore some options to get something similar to this. Our goal will be to take an emacs-lisp file, and generate an org-file of documentation, and then convert that to PDF for reading.

Say we have a function, jmax-bibtex-next-entry, and we want some information about it. Here are three functions that give us the argument list, documentation string, and function definition.

(help-function-arglist 'jmax-bibtex-next-entry)
&optional n
(documentation 'jmax-bibtex-next-entry)
Jump to the beginning of the next bibtex entry. N is a prefix
argument. If it is numeric, jump that many entries
forward. Negative numbers do nothing.
(symbol-function 'jmax-bibtex-next-entry)
lambda (&optional n) Jump to the beginning of the next bibtex entry. N is a prefix\nargument. If it is numeric, jump that many entries\nforward. Negative numbers do nothing. (interactive P) (if (= (point) (save-excursion (bibtex-beginning-of-entry))) (progn (forward-char) (bibtex-next-entry))) (if (re-search-forward bibtex-entry-head nil t (and (numberp n) n)) (progn (bibtex-beginning-of-entry)))

That will not always be the code we wrote, but it is functionally similar.

So we could create an org-entry like this:

(defun fun2org (function-symbol)
  (let ((args (help-function-arglist function-symbol))
        (doc  (documentation function-symbol))
        (code (symbol-function function-symbol)))
    (format "** %s %s
%s

#+BEGIN_SRC emacs-lisp
%S
#+END_SRC
" function-symbol args doc code)))

(fun2org 'jmax-bibtex-next-entry)
** jmax-bibtex-next-entry (&optional n)
Jump to the beginning of the next bibtex entry. N is a prefix
argument. If it is numeric, jump that many entries
forward. Negative numbers do nothing.

#+BEGIN_SRC emacs-lisp
(lambda (&optional n) "Jump to the beginning of the next bibtex entry. N is a prefix
argument. If it is numeric, jump that many entries
forward. Negative numbers do nothing." (interactive "P") (if (= (point) (save-excursion (bibtex-beginning-of-entry))) (progn (forward-char) (bibtex-next-entry))) (if (re-search-forward bibtex-entry-head nil t (and (numberp n) n)) (progn (bibtex-beginning-of-entry))))
#+END_SRC

The code is not that beautifully indented, but it is optional.

For variables, there are similar functions to get their documentation:

(documentation-property 'jmax-bibtex-journal-abbreviations 'variable-documentation)
List of (string journal-full-name journal-abbreviation). Find abbreviations at http://cassi.cas.org/search.jsp.

The problem still is, you have to know the variable and function names in advance. I want to take a file, and generate this for each function, and variable.

I posted a question on StackOverflow on how to get the functions defined in a file. The most feasible suggestion was to use the variable load-history, which contains a history of the variables and functions loaded, and the files they are in.

Here is an example of getting the entries associated with jmax-bibtex.el

(cdr (assoc "/Users/jkitchin/Dropbox/kitchingroup/jmax/jmax-bibtex.el" load-history ))
(jmax-bibtex-journal-abbreviations
 (defun . jmax-bibtex-generate-longtitles)
 (defun . jmax-bibtex-generate-shorttitles)
 (defun . jmax-stringify-journal-name)
 (defun . jmax-set-journal-string)
 jmax-nonascii-latex-replacements
 (defun . jmax-replace-nonascii)
 jmax-lower-case-words
 (defun . jmax-title-case-article)
 (defun . jmax-sentence-case-article)
 (defun . jmax-bibtex-next-entry)
 (defun . jmax-bibtex-previous-entry)
 (defun . jmax-bibtex-mode-keys)
 (provide . jmax-bibtex))

Each element in this case is either a variable, defun or provide. Here, we can use this to print some information about the variables defined in this file. I think it is sufficient to check if the element in the list is a symbol, because all the other elements are cons elements. I suppose there are other possibilities, including defcustom, defgroup, defalias, defsubst, and maybe others.

(dolist (element (cdr
                  (assoc
                   "/Users/jkitchin/Dropbox/kitchingroup/jmax/jmax-bibtex.el"
                   load-history )))
  (when (symbolp element)
    (princ 
    (format "%s
Documentation: %s

" element (documentation-property element 'variable-documentation)))))
jmax-bibtex-journal-abbreviations
Documentation: List of (string journal-full-name journal-abbreviation). Find abbreviations at http://cassi.cas.org/search.jsp.

jmax-nonascii-latex-replacements
Documentation: Cons list of non-ascii characters and their LaTeX representations

jmax-lower-case-words
Documentation: List of words to keep lowercase

We can handle functions by checking if an element is a cons cell with a first element of defun.

(dolist (element (cdr
                  (assoc
                   "/Users/jkitchin/Dropbox/kitchingroup/jmax/jmax-bibtex.el"
                   load-history )))
  (when (and (consp element)
             (eq (car element) 'defun))
    (princ (format "%s is a function\n" (cdr element))))))
jmax-bibtex-generate-longtitles is a function
jmax-bibtex-generate-shorttitles is a function
jmax-stringify-journal-name is a function
jmax-set-journal-string is a function
jmax-replace-nonascii is a function
jmax-title-case-article is a function
jmax-sentence-case-article is a function
jmax-bibtex-next-entry is a function
jmax-bibtex-previous-entry is a function
jmax-bibtex-mode-keys is a function

So, we have the important pieces to mash up what I am looking for. Let us refine the goal. I want to create a PDF documentation of what is in an elisp file with a section on variables, and a section on functions.

(let* ((elements (cdr
                  (assoc
                   "/Users/jkitchin/Dropbox/kitchingroup/jmax/jmax-bibtex.el"
                   load-history)))
       (vars (-filter 'symbolp elements))
       (funcons (-filter (lambda (x)
                           (and (consp x)
                                (eq 'defun (car x))))
                         elements))
       (funcs (mapcar 'cdr funcons)))
  (switch-to-buffer "*org-doc*")
  (erase-buffer)
  (insert (format "#+TITLE: Documentation for %s
#+OPTIONS: toc:nil
\\maketitle
\\tableofcontents
" "/Users/jkitchin/Dropbox/kitchingroup/jmax/jmax-bibtex.el"))
  (insert "* Variables\n")
  (dolist (var (sort vars 'string-lessp))
    (insert (format "** %s
Documentation: %s\n\n" var  (documentation-property var 'variable-documentation))))

  (insert "* Functions\n\n")
  (dolist (funcs (sort funcs 'string-lessp))
    (insert (format "** %s %s
Documentation: %s

Code:
#+BEGIN_SRC emacs-lisp
%S
#+END_SRC
"
                    funcs
                    (or (help-function-arglist funcs) "")
                    (documentation funcs)
                    (symbol-function funcs))))

  (org-mode)
  (write-file "jmax-bibtex-doc.org")
  (org-export-to-file 'latex "jmax-bibtex-doc.tex")
  (org-latex-compile "jmax-bibtex-doc.tex")
  (kill-buffer "*org-doc*")
  (kill-buffer "jmax-bibtex-doc.org"))

Here is the resulting pdf: jmax-bibtex-doc.pdf . It is not too bad. The code is not beautiful, and it would take some work to get that looking nice. It might be nice to find all instances of '` and replace them with links to variable names, but I leave that for another day. There is also no information about the header comments, but I leave this for another day to.

Copyright (C) 2014 by John Kitchin. See the License for information about copying.

org-mode source

Org-mode version = 8.2.7c

Discuss on Twitter