I use org-mode to take a lot of notes and I frequently want to highlight some terms that I’m defining or a key sentence. Overall, I want two things:
- I would like to add some markup syntax which will highlight a word or phrase, using ':' to mark the start and end of the phrase (similar to bold or italic markup).
- When I export this markup through HTML (or markdown) it the markup should get converted to the HTML5
Extending the org syntax isn’t the most straightforward. At first, I tried to modify the
org-emphasis-alist variable, but that didn’t seem to work. Instead, I was able to extend
org-font-lock with an extra regex.
(defun org-add-my-extra-markup () "Add highlight emphasis." (add-to-list 'org-font-lock-extra-keywords '("[^\\w]\\(:\\[^\n\r\t]+:\\)[^\\w]" (1 '(face highlight invisible nil))))) (add-hook 'org-font-lock-set-keywords-hook #'org-add-my-extra-markup)
The elisp regular expression isn’t the easiest to understand, but this is what it matches:
||something that’s not a word|
||start of a capture group (not matching)|
||a string of 1 or more characters that doesn’t contain new-lines or tabs|
||end of the capture group (not matching)|
||something that’s not a word|
The start-and-end matching of something that’s not a word means that colons in the middle of a URL are not matched and that the highlighted text can be followed by punctuation. The parenthesis create a capture group, which is used later to define what has the highlight face.Here is a diagram to try and explain the different pieces:
'("[^\\w]\\(:\\[^\n\r\t]+:\\)[^\\w]" (1 '(face highlight invisible nil))) | '--capture group---' | | '--face definition-----------' '--regular expression to match---' '--capture group number
The next step is to extend the org-exporter. This is easily done and a working example is even provided by export engine manual. I simply create a filter for any backend built on top of html (which should include my markdown and jekyll-md exporters).
(defun my-html-mark-tag (text backend info) "Transcode :blah: into <mark>blah</mark> in body text." (when (org-export-derived-backend-p backend 'html) (let ((text (replace-regexp-in-string "[^\\w]\\(:\\)[^\n\t\r]+\\(:\\)[^\\w]" "<mark>" text nil nil 1 nil))) (replace-regexp-in-string "[^\\w]\\(<mark>\\)[^\n\t\r]+\\(:\\)[^\\w]" "</mark>" text nil nil 2 nil)))) (add-to-list 'org-export-filter-plain-text-fucntions 'my-html-mark-tag)
I add this to my
org-config.el file and success!
Of course, org-mode already ships with some built-in highlighting functionality; namely, the syntax for defining a description list (which is a native HTML concept). The following org formatting can be used to create a description list:
- Emacs :: an extensible ,customizable, free/libre text editor - Org mode :: keeping notes, maintaining TODO lists, planning projects, and authoring documents with a fast and effective plain-text system
Which becomes the following HTML:
<dl> <dt>Emacs</dt><dd>an extensible ,customizable, free/libre text editor</dd> <dt>Org mode</dt><dd>keeping notes, maintaining TODO lists, planning projects, and authoring documents with a fast and effective plain-text system</dd> </dl>
While this is a common use-case for highlighting, I don’t always want my highlights to be in a list.
Here is a very brief example of a highlighted phrase which was exported properly. I can also use this method to highlight a longer sentence, such as: Should I implement this highlighting syntax and export functionality into Orgmode itself?