Recently I’ve migrated my blog from Jekyll to Org mode. I’m not gonna write down the entire process, cause there are plenty of articles about that on the internet already. Although if you are interested, the sources of this blog can be found online.
This post is the first in a series of posts about problems I’ve encountered and no one on the internet seemed to have fixed already. It’s about clickable headlines.
When Org mode publishes files to HTML, it adds the
id attribute to
all headlines, making them anchors to link to. But when someone wants
to copy the link to a headline, they’ll need to web-inspect the page
to find the id. So most blogging engines make the headlines clickable,
with a link to the headline self. Unfortunately, Org mode does not do
that out of the box.
When you define the
org-publish-project-alist, you can specify the
:html-format-headline-function property. This property should be set
to a function taking the following six arguments:
- the todo keyword (string or
- the type of todo (symbol:
- the priority of the headline (integer or
- the main headline text (string)
- the tags (string or
- the export options (plist)
By default this property is set to
So I attempted to write a function that calls the original function,
text as a hyperlink.
This shouldn’t be too hard, although there was one problem, where do I
get the value of the
It took me a lot of debugging to figure this out. I started inspecting
the values of the six arguments, and I noticed something weird was
going on with the
#("Introduction" 0 12 (:parent (headline (:raw-value "Introduction" :begin 105 :end 507 :pre-blank 0 :contents-begin 121 :contents-end 506 ...
The variable contains the text of the headline, but apparently also some other things.
I tried to inspect the type of the variable:
But that didn’t help me much…
I could not understand what
#( meant. So I started searching the web
and eventually I stumbled on a StackOverflow answer. It turns out, and
I really didn’t know that, strings in Emacs lisp can have Text
In the example above, there is a text property
0 with length
Apparently, this property contains the headline element. And Org mode
has functions to read properties of that element. All what remained to
do was extract the
:ID property and wrap the
<a href...> &
Eventually I ended up with this function:
(defun my-org-html-format-headline-function (todo todo-type priority text tags info) "Format a headline with a link to itself." (let* ((headline (get-text-property 0 :parent text)) (id (or (org-element-property :CUSTOM_ID headline) (org-export-get-reference headline info) (org-element-property :ID headline))) (link (if id (format "<a href=\"#%s\">%s</a>" id text) text))) (org-html-format-headline-default-function todo todo-type priority link tags info)))
You can find the exact function I’m using here.
And then all you need to do is specify this function in your
(setq org-publish-project-alist (list (list "blog-posts" :base-directory "posts" :base-extension "org" ;; ... :html-format-headline-function 'my-org-html-format-headline-function ;; ... ) ;; ... ))
Comments are welcome on Reddit.