How to add extra attributes to link in Org-mode?


In general, Org has a generic "solution" for this task:

#+ATTR_HTML: :rel nofollow
Lorem ispum [[][dolor]]!

That will be exported to:

<p rel="nofollow">Lorem ispum <a href="" rel="nofollow">dolor</a>!</p>

That in my very honest opinion it looks really awful.


I've decided to slightly change the link syntax but without breaking backwards compatibility:

[[|:rel nofollow :title View]][Here is my link]]

This should be exported to:

<a href="" rel="nofollow" title="View">Here is my link</a>

Looks a way better than the default solution. Even if end-user will user vanilla org-mode to export this piece all should be workable except the link anchor that's is not critical in most cases.


After long looking through the endless code of the Org I found a function that is responsible for parsing link and providing link object —org-element-link-parser.

To add extra parameters to parser I've used advice :filter-retun— it returns the value for the original function to the advice-function for the further processing.

(require 's)
(require 'dash)

(defun pelican/org-link-extra-attrs (orig-fun &rest args)
  "Post processor for parsing links"
  (setq parser-result orig-fun)

  ;;; Retrieving inital values that should be replaced
  (setq raw-path (plist-get (nth 1 parser-result) :raw-link))
  (setq path (plist-get (nth 1 parser-result) :raw-link))

  ;; Checking if link match the regular expression
  (if (string-match-p "^https?://.*|\s?:" raw-path)
	;; Retrieving and decoding parameters after the vertical bar
	(setq results (s-split "|" (url-unhex-string raw-path)))
	;; URL cleanup
	(setq raw-path (car results))
	(setq path (car (s-split "|" path)))

	;; Splitting elements by colon and remove any empty values
	(setq results (--drop-while (< (length it) 1)
				    (s-split ":" (car (-slice results 1)))))
	;; Splitting key and value and trimming any spaces
	(setq results (--map  (s-split-up-to "\s" (s-trim it) 1) results))

	;; Updating the ouput with the new values
	(setq orig-fun-cleaned (plist-put (nth 1 orig-fun) :raw-link raw-path))
	(setq orig-fun-cleaned (plist-put orig-fun-cleaned :path path))

	(list 'link
	      (-snoc orig-fun-cleaned :extra-attrs results)))

	 ;; Or returning original value of the function

(advice-add 'org-element-link-parser :filter-return #'pelican/org-link-extra-attrs)

Now we need to add the support of the new attributes to the parser for the HTML exporting backend.

(defun pelican/link-html (link desc info)
  "Custom syntax processor for extra link atrributes
  Syntax: [[|:attr value][Link text]]"

  (setq extra-attrs (org-element-property :extra-attrs link))
  (setq processed-link-html (org-html-link link desc info))

  ;; If link has any atrributes
  (if extra-attrs
	;; Creating string formated as key="value"
	(setq extra-attrs
	      (s-trim (--reduce-from (concat acc " "
					     (format "%s=%s" (nth 0 it)
						     (s-wrap (nth 1 it)  "\""))) "" extra-attrs)))

	;; Inserting atrribute string to link tag
	(setq insert-point (string-match ">" processed-link-html))
	(setq processed-link-html
	      (concat (substring  processed-link-html 0 insert-point)
		      (substring processed-link-html insert-point))))
    ;; HTML output

;; Custom backend settings
(org-export-define-derived-backend 'pelican-html 'html
  :translate-alist '((link . pelican/link-html)))
comments powered by Disqus