Adding :target option for the TOC keyword in Org Mode
| emacs, orgNow that A- can be counted on to happily play with a babysitter for several hours once a week, I’ve decided to alternate consulting and personal projects. Two weeks ago, I used my personal time to make a script that renewed my library books automatically. This week, I set aside time to look at Org Mode. DC had asked me to update the patch I made to allow people to specify a target for the table of contents, and I was curious about whether I could hack something together.
Patch for adding :target to TOC keyword
Here’s a sample file that shows what I mean:
* Not this section ** Heading X ** Heading Y * Target :PROPERTIES: :CUSTOM_ID: TargetSection :END: ** Heading A ** Heading B * Another section
Here’s the core of how to make it work for HTML exports:
(defun org-html-keyword (keyword _contents info) "Transcode a KEYWORD element from Org to HTML. CONTENTS is nil. INFO is a plist holding contextual information." (let ((key (org-element-property :key keyword)) (value (org-element-property :value keyword))) (cond ((string= key "HTML") value) ((string= key "TOC") (let ((case-fold-search t)) (cond ((string-match "\\<headlines\\>" value) (let ((depth (and (string-match "\\<[0-9]+\\>" value) (string-to-number (match-string 0 value)))) (scope (cond ;; link ((string-match ":target +\"\\([^\"]+\\)\"" value) (let ((link (with-temp-buffer (save-excursion (insert (org-make-link-string (match-string 1 value)))) (org-element-link-parser)))) (pcase (org-element-property :type link) ((or "custom-id" "id") (org-export-resolve-id-link link info)) ("fuzzy" (org-export-resolve-fuzzy-link link info)) (_ nil)))) ;; local ((string-match-p "\\<local\\>" value) keyword)))) (org-html-toc depth info scope))) ((string= "listings" value) (org-html-list-of-listings info)) ((string= "tables" value) (org-html-list-of-tables info))))))))
It was a lot of fun Doing the Right Thing(s): writing documentation, adding tests, and making it work for more than just HTML export. I found out where to make the changes by using grep
to search for TOC
in the Org Mode source code. All the heavy lifting was already done by org-export-collect-headlines
, so it was just a matter of passing the right scope
. It took me a while to figure out that I needed to pass an Org link element. An easy way of making that element work for both fuzzy and ID-specific links was to insert the target text into a temporary buffer (remembering to use org-make-link-string
) and then calling org-element-link-parser
.
I tried figuring out how to make it work with a link to another file, but I didn’t get very far, so I figured I’d just wrap things up nicely there.
I wasn’t sure if my original post made it through because I sent it through Gmane and Cc:d DC, who got it with an empty To:, so I ended up submitting it twice. I just realized I forgot to add test-ox-ascii.el
. I don’t want to spam the list, so I’ll send that along with other changes if people have feedback.
But look! Open source contributions! I’m so excited. I wonder what I’ll get to do in two weeks from now. =)