how to make rainbow-delimiters-mode work with org-mode export or htmlize

1. problem

You customize rainbow-delimiters so that it is used with emacs lisp buffers at least.

(add-hook 'emacs-lisp-mode-hook 'rainbow-delimiters-mode)

and you write an org-mode buffer with an elisp src block (code block):

#+BEGIN_SRC elisp

and you export the buffer to html by pressing C-c C-e (bound to org-export-dispatch). You expect to see the effect of rainbow-delimiters-mode in the resulting html file, but the html file doesn’t show rainbow delimiters.

2. one solution

This code will patch things up so that rainbow-delimiters-mode work with org html export feature and htmlize.

(eval-after-load 'htmlize
     ;; make htmlize to handle face name strings as well
     (defadvice htmlize-attrlist-to-fstruct (around my-make-it-accept-string activate)
       (if (stringp (ad-get-arg 0))
             (setq ad-return-value (htmlize-face-to-fstruct (intern (ad-get-arg 0)))))

(defvar my-htmlize-off-modes nil
  "list of minor modes to disable when using htmlize")

(defun my-htmlize-before-hook-default ()
  (dolist (mode my-htmlize-off-modes)
    (if (fboundp mode)
        (funcall mode 0)))


  ;; copied from font-lock-default-function (make font-lock-face property act as alias for face property)
  (set (make-local-variable 'char-property-alias-alist)
       (copy-tree char-property-alias-alist))
  (let ((elt (assq 'face char-property-alias-alist)))
    (if elt
        (unless (memq 'font-lock-face (cdr elt))
          (setcdr elt (nconc (cdr elt) (list 'font-lock-face))))
      (push (list 'face 'font-lock-face) char-property-alias-alist))))

(add-hook 'htmlize-before-hook 'my-htmlize-before-hook-default)

;; (add-to-list 'my-htmlize-off-modes 'rainbow-delimiters-mode)

This solution is dirty because it relies on a copy of part of undocumented internal details of font-lock-default-function.

3. causes

This problem is caused by many bugs or non-bugs spread in many places, and it also turns out that htmlize provides a hook just right for patching things up: htmlize-before-hook.

There are many reasons why the current version 1.3.4 of rainbow-delimiters does not work with org export (org-mode version 8.0.7) and htmlize (20130207.2102).

One is that rainbow-delimiters-mode sets font-lock-face on buffer text using face name strings rather than face name symbols, and the current version of htmlize cannot work with face name strings. Older version of htmlize used to, and I don’t know why it’s changed. This is why when one calls htmlize-buffer on a rainbow-delimiters enabled elisp buffer, one will get an error like:

eq: Wrong type argument: listp, "rainbow-delimiters-depth-9-face"

Another reason is the use of with-temp-buffer by htmlize and the org mode html export feature (implemented in ox-html.el). Problem with with-temp-buffer here is that it uses a temp buffer with name starting with a space, and font-lock-mode avoids working with such temp buffers. Turning on font-lock-mode on a normal buffer causes the value of font-lock-function (which usually is font-lock-default-function) to be called, and that doesn’t happen with temp buffers. What font-lock-default-function does is it makes the text property font-lock-face to act as an alias for the text property face, and some other things.

Third reason is that jit-lock-fontify-now is not getting called. There is a call to jit-lock-fontify-now in htmlize.el and yet it’s not getting called. I don’t know why.

4. what official fixes might look like

htmlize should be changed to succeed to call (jit-lock-fontify-now) and abandon the use of with-temp-buffer. Speaking of htmlize, on a side note, it would be nice if htmlize could work with a face which is simply a list of face name symbols because that would solve a little problem with using fixed-with font in org-mode.

rainbow-delimiters should be changed to use face name symbols. On a side note, currently rainbow-delimiters rotates through 9 faces, it would be nice if it provides an option to rotate through less than 9 faces, and an option to make the top level paren special, that is, use one special face for the top level parens, and then start rotating with the rest of 8 faces for deeper parens. Maybe two options can be combined/generalized into one user option (which can be buffer local) by taking an idea from repeating decimal notation. Maybe I will come up with some code.

ox-html in the org package should abandon the use of with-temp-buffer and provide its own official analog of htmlize-before-hook and my-htmlize-off-modes so that users can use them to turn off rainbow-delimiters-mode or others on html export if they want to.

5. closing thoughts

Even if the bugs don’t get fixed, one can get by thanks to the hook variable htmlize-before-hook, as seen here. The author of htmlize doesn’t really have to implement a feature request such as “give us a user option to turn off some minor modes for htmlize” or a bug fix request such as “ensure a call to jit-lock-fontify-now” because htmlize-before-hook can already take care of the two requests. When an Emacs package author decides to provide a hook, he is knowingly or unknowingly taking care of a lot of feature requests from the future. This is why hooks are great things.

This entry was posted in Emacs and tagged , , , , . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s