Time-stamp: <2021-03-11 14:34>

Emacs, Org-mode, and Gemini

There is some discussion on the gemini email list about rendering tables and such. Personally, I'm not in favor of adding especially this to the Gemini file syntax -- we all know what happened with tables in HTML. On the other hand, it would be nice to have a richer display of content including tables proper and e.g. source code with syntax highlighting.

I'm using Elpher in Emacs to browse Gemspace. I'm also using Germinal to serve my capsule, a Gemini server written in Common Lisp. It just so happens that a mime type for org-mode is predefined in Germinal. So why not just use Org for the fancy stuff?

By default, elpher displays Org files as plain text. Just switching to Org-mode kills the browsing session. Org files from unknown sources can pose a certain danger as well because they can contain code to be evaluated when the user set org-confirm-babel-evaluate to nil.

My Elpher configuration opens an org file in a new buffer, sets org-confirm-babel-evaluate to t for the buffer, regardless of system-wide setting, and uses a keymap that allows to go back to elpher or save the file to disk. The file is also opened read-only. That, I hope, makes it safe enough. Here's the configuration:

(use-package elpher
  :ensure t
  :config
  (use-package elpher
  :ensure t
  :config
  (defun elpher-render-org (data &optional _mime-type-string)
    "Render DATA as Org by opening a new buffer and switching to Org mode"
    (let ((buf (generate-new-buffer "elpher-org")))
      (with-current-buffer buf
        (if (not data)
            t
          (progn (switch-to-buffer buf)
                 (setq elpher-org-map (make-sparse-keymap))
                 (define-key elpher-org-map (kbd "b") (lambda () (interactive)
                                                        (kill-current-buffer)
                                                        (switch-to-buffer "*elpher*")
                                                        (elpher-back)))
                 (define-key elpher-org-map (kbd "W") (lambda () (interactive) (save-buffer default-directory t)))
                 (make-local-variable 'org-confirm-babel-evaluate)
                 (setq org-confirm-babel-evaluate t)
                 (org-mode)
                 (insert data)
                 (view-mode t)
                 (use-local-map elpher-org-map)
                 (message "Use W to save to file, b to return to elpher. View-mode keybindings apply."))))))
  (defun elpher-render-gemini (body &optional mime-type-string)
    "Render gemini response BODY with rendering MIME-TYPE-STRING."
    (if (not body)
        t
      (let* ((mime-type-string* (if (or (not mime-type-string)
                                        (string-empty-p mime-type-string))
                                    "text/gemini; charset=utf-8"
                                  mime-type-string))
             (mime-type-split (split-string mime-type-string* ";" t))
             (mime-type (string-trim (car mime-type-split)))
             (parameters (mapcar (lambda (s)
                                   (let ((key-val (split-string s "=")))
                                     (list (downcase (string-trim (car key-val)))
                                           (downcase (string-trim (cadr key-val))))))
                                 (cdr mime-type-split))))
        (when (string-prefix-p "text/" mime-type)
          (setq body (decode-coding-string
                      body
                      (if (assoc "charset" parameters)
                          (intern (cadr (assoc "charset" parameters)))
                        'utf-8)))
          (setq body (replace-regexp-in-string "\r" "" body)))
        (pcase mime-type
          ((or "text/gemini" "")
           (elpher-render-gemini-map body parameters))
          ("text/html"
           (elpher-render-html body))
          ("text/org-mode"
           (elpher-render-org body))
          ((pred (string-prefix-p "text/"))
           (elpher-render-gemini-plain-text body parameters))
          ((pred (string-prefix-p "image/"))
           (elpher-render-image body))
          (_other
           (elpher-render-download body)))))))

Download elisp source

Prerequisite for this to work is that the server returns a mime type of "text/org-mode" for an Org file.

Try this with the above configuration:

An Org Test file with a table

----------

language: EN

tags: emacs lisp gemini org-mode