Skip to main content

Theresa O’Connor / Treasa Ní Chonchúir

Transforming Markdown links into HTML while editing

I write a lot of Markdown by hand. Links in Markdown look like this:

[This is a link to Zombo com](https://zombo.com/)
A Markdown link

When a Markdown processor sees the above, it transforms it into an <a> element like so:

<a href=https://zombo.com/>This is a link to Zombo com</a>
The HTML generated by that link

Most of the time this is all you need. But the <a> element takes many attributes. When I want to add one—rel=nofollow, say, or download=filename.ext—I have to rewrite the link into HTML myself, because Markdown doesn’t have a syntax for attributes like these.

Transforming Markdown links to HTML is laborious to do by hand, so I wrote a bit of elisp to do it for me:

Define a transform-link-at-point command
Load dependencies
(defun transform-link-at-point (point)
  "Turn a Markdown link or image at `point' to an HTML element."
  (interactive "d")
  (goto-char point)
  Only proceed if we're in the middle of a Markdown link
  (let (Use intuitive names for the components of a Markdown link)
    Remove the Markdown link
    Insert the HTML equivalent
    Reposition cursor before the >))

transform-link-at-point is a command that operates at the current point. Iff point is inside a Markdown link, we remove the old Markdown syntax, insert the HTML equivalent, and reposition the cursor to be right before the > character of the open tag. (After all, the whole point of this hack is to make it more efficient to add attributes to links.)

Deleting the Markdown link is very easy:

Remove the Markdown link
(delete-region start end)

Creating an <a> element is pretty easy too:

Create an <a> element
(format "<a href=%s%s>%s</a>" url title-attr text)

We should also handle images; it’s not much more work. Markdown’s image syntax is basically the same at its link syntax, just with a ! character at the start:

![Alt text](/path/to/img.jpg)
A Markdown image

Fortunately creating an <img> element is just as easy as creating a link:

Create an <img> element
(format "<img src=%s alt=\"%s\"%s>" url text title-attr)

So we figure out which element to use & then insert it:

Insert the HTML equivalent
(insert
 (if is-image
     Create an <img> element
   Create an <a> element))

Of course, we shouldn’t attempt to do this unless we’re actually inside a Markdown link:

Only proceed if we're in the middle of a Markdown link
(unless (thing-at-point-looking-at markdown-regex-link-inline)
  (error "Not in a Markdown link."))

thing-at-point-looking-at is a predicate that returns t if the cursor is currently inside the kind of thing you’re interested in, and markdown-regex-link-inline is a regexp that matches Markdown links and images. They’re defined in thingatpt.el (included in Emacs) and markdown-mode.el, respectively:

Load dependencies
(require 'thingatpt)
(require 'markdown-mode)

When thing-at-point-looking-at succeeds, Emacs’ match data contains all of the various components of the Markdown link we’re in the middle of. Let’s give those components humane names so the rest of the code makes sense:

Use intuitive names for the components of a Markdown link
(start (match-beginning 0))
(end (match-end 0))
(is-image (match-string 1))
(text (match-string 3))
(url (match-string 6))
(title-attr (if (match-string 7)
                (format " title=%s" (match-string 7))
              ""))

The last thing we do is position the cursor so I can quickly add more attributes to the element.

Reposition cursor before the >
(goto-char start)
(search-forward ">" nil t)
(backward-char 1)