Self-hosted org-mode tools

2025/01/09

I have a couple org-mode based tools now:

Each of these has an API heading that defines interactive helpers relevant for the file. E.g.:

* COMMENT API
#+NAME: api
#+begin_src elisp
(define-minor-mode org-books-mode
  "Helper functions for books.org"
  :interactive nil
  :keymap (list
           (cons (kbd "C-c C-o") #'org-books-open-goodreads)))

(defun org-books-open-goodreads ()
  "Open goodreads for current entry."
  (interactive nil 'org-books-mode)
  (browse-url (format "https://goodreads.com/book/show/%s"
                      (cdar (org-entry-properties nil "goodreadsid")))))

(org-books-mode 1)
#+end_src

# Local Variables:
# eval: (sarg/eval-org-src-block "api")
# End:
Code Snippet 1: API section

Where sarg/eval-org-src-block is defined as:

(defun sarg/eval-org-src-block (name)
  "Eval elisp block NAME. To be used in org files in Local variables section."

  (save-excursion
    (org-babel-goto-named-src-block name)
    (let* ((block-info (org-babel-get-src-block-info))
           (lang (nth 0 block-info))
           (body (nth 1 block-info)))
      (if (or (string= lang "emacs-lisp")
              (string= lang "elisp"))
          (cl-loop with next = 0
                   with maxlen = (length body)
                   for (sexp . next) = (read-from-string body next)
                   do (eval sexp)
                   while (< next maxlen))

        (error "%s is not an emacs-lisp src block" name)))))
Code Snippet 2: sarg/eval-org-src-block

This way when such org file is opened, the API code block gets loaded and a buffer-local minor mode gets activated. The mode is marked as :interactive nil so it doesn’t pollute M-x. Helper functions are restricted to this mode as well with (interactive nil 'org-books-mode). I like that such an “API” is contained in the file itself, making it a kind of self-hosted program.