index rss mastodon twitter github linkedin email
Álvaro Ramírez
sponsor

Álvaro Ramírez

28 April 2023 Generating elisp org docs

chatgpt-shell's README includes few org tables documenting the package's customizable variables as well as available commands. Don't worry, this isn't really another ChatGPT post.

Here's an extract of the docs table:

| Custom variable                       | Description                                                 |
|---------------------------------------+-------------------------------------------------------------|
| chatgpt-shell-display-function        | Function to display the shell.                              |
| chatgpt-shell-curl-additional-options | Additional options for `curl' command.                      |
| chatgpt-shell-system-prompt           | The system message helps set the behavior of the assistant. |

While the table docs didn't take long to build manually, they quickly became out of sync with their elisp counterparts. Not ideal, as it'll require a little more careful maintenance in the future.

Emacs being the self-documenting editor that it is, I figured I should be able to extract customizable variables, commands, along with their respective docs, and generate these very same org tables.

I had no idea how to go about this, but apropos-variable and apropos-command surely knew where to fetch the details from. A peak into apropos.el quickly got me on my way. Turns out mapatoms is just what I needed. It iterates over obarray, Emacs's symbol table. We can use it to extract the symbols we're after.

Since we're filtering symbols from chatgpt-shell, we can start by including only those whose symbol-name match "^chatgpt-shell". Out of all matching, we should only keep custom variables. We can use custom-variable-p to check for that. This gives us all relevant variables. We can subsequently get each variable's corresponding docs using (get symbol 'variable-documentation) and put it into a list.

Now, if we pull our org babel rabbit out of our Emacs magic hat, we can use :results table to print the list as an org table. The source block powering this magic trick looks as follows:

#+begin_src emacs-lisp :results table :colnames '("Custom variable" "Description")
  (let ((rows))
    (mapatoms
     (lambda (symbol)
       (when (and (string-match "^chatgpt-shell"
                                (symbol-name symbol))
                  (custom-variable-p symbol))
         (push `(,symbol
                 ,(car
                   (split-string
                    (or (get (indirect-variable symbol)
                             'variable-documentation)
                        (get symbol 'variable-documentation)
                        "")
                    "\n")))
               rows))))
    rows)
#+end_src

And just like that… we effortlessly get our elisp docs in an org table, straight from Emacs's symbol table.

docs.gif

It's worth noting that our snippet used indirect-variable to resolve aliases but also limited descriptions to the first line in each docstring.

To build a similar table for interactive commands, we can use the following block (also including bindings).

#+BEGIN_SRC emacs-lisp :results table :colnames '("Binding" "Command" "Description")
  (let ((rows))
    (mapatoms
     (lambda (symbol)
       (when (and (string-match "^chatgpt-shell"
                                (symbol-name symbol))
                  (commandp symbol))
         (push `(,(mapconcat
                   #'help--key-description-fontified
                   (where-is-internal
                    symbol shell-maker-mode-map nil nil (command-remapping symbol)) ", ")
                 ,symbol
                 ,(car
                   (split-string
                    (or (documentation symbol t) "")
                    "\n")))
               rows))))
    rows)
#+END_SRC

commands.gif

You see? This post wasn't really about ChatGPT. Aren't you glad you stuck around? 😀