Flowing Markdown reference links

It’s not exactly Emacs vs. vi, but the two styles of linking in Markdown, reference and inline, have their adherents, and each makes their case. In the reference corner, John Gruber:

… I personally much prefer the reference style… the reference style is just so much more readable.

And in the inline corner, Jason Snell:

But when I’m writing, pasting in URLs as I’m typing not only matches the way it’s done in HTML, it also allows me to keep my writing flow as much as possible. I type the text, I paste the link, and I keep writing. Naming a link, moving down, adding a link line, and then returning up to where I was—it breaks the flow.

Both are correct. Reference links are more readable1, but they take more time to write—at least if you’re writing in a dumb editor. When I make a Markdown link in an online forum, like the Automators forum, I always use inline links because I’m writing in an HTML text field, which is the dumbest of text editors.

But when I’m writing a blog post, I’m doing it in BBEdit, which has an AppleScript dictionary that can handle all of the cursor movement and pasting that Jason (rightly) dislikes. I don’t waste time or lose my train of thought in a lot of mousing around.

In the typical case, the web page I want to link to is the front page in Safari. To link some text to that page, I select the anchor text

BBEdit with anchor text selected

and then type ⌃L to run my Safari Front Link script. It gets the URL of the front page in Safari, adds it to the bottom of the document with an incremented reference number, and surrounds the anchor text with brackets and the reference. The cursor is placed after the reference.

BBEdit with reference link added

If there’s no text selected, Safari Front Link does pretty much the same thing, but the anchor text is empty and the cursor is placed between the brackets for me to type it in.

Two scripts do the work. The first is a Python script called nextreflink, which reads its input and returns the number of the next reference link. In the example above, because 5 was the last reference link number when the script was called, nextreflink returned 6. Here’s the source code:

python:
 1:  #!/usr/bin/python
 2:  
 3:  import sys
 4:  import re
 5:  
 6:  text = sys.stdin.read()
 7:  reflinks = re.findall(r'^\[(\d+)\]: ', text, re.MULTILINE)
 8:  if len(reflinks) == 0:
 9:    print 1
10:  else:
11:    print max(int(x) for x in reflinks) + 1

You can tell I’ve been using this for a while because it’s written in Python 2.

There’s not much to this script. It scans the text it’s given, finds all the bracketed numbers followed by a colon and a space, determines the maximum of those numbers, and prints out one more than that number. If there are no reference links in the text, it prints 1.

The nextreflink script is used by the Safari Front Link AppleScript, which I have bound to the ⌃L key combination. It does all the tedious cursor movement and text insertion.

applescript:
 1:  -- Get the path to the Resources directory in the package.
 2:  tell application "Finder" to set cPath to container of container of (path to me) as text
 3:  set rPath to (quoted form of POSIX path of cPath) & "../Resources/"
 4:  
 5:  tell application "System Events"
 6:    set pNames to name of every process
 7:    if "Safari" is in pNames then
 8:      try
 9:        tell application "Safari" to set myURL to the URL of the front document
10:      on error
11:        do shell script "afplay /System/Library/Sounds/Funk.aiff"
12:        return
13:      end try
14:    else
15:      do shell script "afplay /System/Library/Sounds/Funk.aiff"
16:      return
17:    end if
18:  end tell
19:  
20:  tell application "BBEdit"
21:    set hereDoc to linefeed & contents of front document & linefeed & "XXXXXXXXXX"
22:    set myRef to do shell script rPath & "nextreflink <<XXXXXXXXXX" & hereDoc
23:    
24:    if length of selection is 0 then
25:      -- Add link with empty text and set the cursor between the brackets.
26:      set curPt to characterOffset of selection
27:      select insertion point before character curPt of front document
28:      set selection to "[][" & myRef & "]"
29:      select insertion point after character curPt of front document
30:      
31:    else
32:      -- Turn selected text into link and put cursor after the reference.
33:      add prefix and suffix of selection prefix "[" suffix "]" & "[" & myRef & "]"
34:      select insertion point after last character of selection
35:    end if
36:    
37:    -- Add the reference at the bottom of the document and reset cursor.
38:    set savePt to selection
39:    select insertion point after last character of front document
40:    set selection to "[" & myRef & "]: " & myURL & return
41:    select savePt
42:    activate
43:  end tell

Lines 2–3 are a little tricky. I have both of these scripts saved in a BBEdit package (which is basically just a directory), and these two lines figure out the subdirectory where nextreflink is saved and put that path into the rPath variable. rPath gets used later when we call nextreflink. If you want to use this system, you’ll have to figure out your own way to set rPath.

The next chunk of code gets the URL of the front page in Safari. That’s Line 9. The rest of it is just error handling code. A warning sounds if Safari isn’t running or if it’s running but has no open window.

The rest of the code does all the cursor movement and text insertion. First, Lines 21–22 send the text of the current BBEdit document to nextreflink and save the number returned into the myRef variable.2 If there’s no text selected in the document, Lines 26–29 insert a blank pair of brackets followed by the reference number in brackets at the cursor location. If there is text selected, Lines 33–34 surround it with brackets and put the bracketed reference number after it. Lines 39–40 add the reference number and URL to the bottom of the document, and Line 41 moves the cursor back up to where we were writing.

I’ve had similar systems set up for adding reference links in every editor I’ve used for writing blog posts. If an editor isn’t programmable enough to do this, I won’t use it for blogging.

There is another option for getting the ease of writing with inline links while ending up with the readability of reference links: Brett Terpstra’s Markdown Service Tools include a script that converts inline links to reference links. I don’t use Brett’s tools because I’ve built up my own over the years, but they get good reviews from those who do.

One last thing: Jason refers to inline links as the “lazy” style. I think my way of making reference links is just as lazy.


  1. Jason argues that inline links are easier to edit because the URL is right next to the anchor. I would argue that link-checking in the raw Markdown source is holding it wrong. It’s much easier to check links by clicking them after the Markdown has been processed and opened in a web browser. 

  2. The text is passed to nextreflink through a shell construct called a here document