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:

 1:  #!/usr/bin/python
 3:  import sys
 4:  import re
 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.

 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/"
 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
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
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
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
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

Siri and numbers

Several years ago, I wrote a post about using Siri to dictate measurements as I take them. I still do that, and it still works very well because Siri is fast and accurate at transcribing numbers. By using Siri as a sort of secretary, I don’t have to switch between my measuring tools and a notebook, and I don’t have to worry about mistyping a number on my phone’s keyboard. Over the past year, I’ve learned a new Siri trick with numbers: addition.

In my job, I often need to go through a set of drawings of a building or a machine, determine how many times a certain feature or part is used on each drawing, and then add them up. This obviously isn’t hard work, but it’s dull, and it’s easy to make mistakes when work is dull. But Siri doesn’t care if the work is dull, and because of the way it answers questions, it provides an automatic error-checking system.

Let’s say I have a list of numbers to add—all the Grade 5 bolts used in a machine housing, for example. I say, “Hey, Siri, what’s seven plus twelve plus fourteen plus four plus…” and wait for the answer. Siri responds, “Seven plus twelve plus fourteen plus four plus… is 73.” I not only get the result without a math error, I also get an echo of all the individual numbers, so I can make sure that Siri understood them all1 and that I didn’t skip or misread any of them. It’s faster and more accurate than I can do it with a calculator or spreadsheet.

There are limits. I’ve found that Siri tunes out and just won’t answer if the list of numbers is too long. Siri can handle lists of over fifteen numbers, but I generally don’t try more than about ten at a time. It’s easy enough to break a long list into sublists that Siri can then add later. Also, dictating out a long list of “A plus B plus C plus D plus…” is tiring and prone to error on my part.

(I do feel a twinge of embarrassment at using voice recognition and network access just to add a column of figures. That’s a lot of computing resources to throw at a such an elementary problem. But it’s a better way of adding.)

As you might expect, Siri can be weirdly persnickety in how you phrase the request. For example, “Hey, Siri, add one and two and three” will get the response “One plus two is three.” Not helpful. But if you ask, “Hey, Siri, what is the sum of one and two and three,” it will tell you what you want.2

I came across this way of getting sums in the past year because of the pandemic. I’ve been working at home, where there’s a HomePod near my desk, and one day it dawned on me that if it could understand my telling it to play “Sketches of Spain,” it could handle simple addition. I doubt I would have ever learned this at the office because

  1. I don’t use Siri on my Mac.
  2. If I pick up my phone to do a calculation, I’m going to open PCalc out of habit.

It was only because I’m used to talking to my HomePod that I thought of doing the sums this way. As things head back to normal and I spend more time at the office, I’ll try to take this lesson with me and do more by talking to my phone and watch.

  1. I’ve sometimes garbled my words badly when doing this, but I’ve never caught Siri in a transcription error. I think the word “plus” makes the context clear, and every number is interpreted as such. 

  2. I prefer saying “and” repeatedly to saying “plus” repeatedly, but I usually forget the proper preamble (I just had to test it to write this paragraph) so I typically go with the “plus” formulation. 


I suspect a lot of us who bought M1 MacBook Airs are thinking about the next generation low-end MacBooks (whether Apple calls them Airs or not) and wondering if maybe we should have waited a year for a step up in Apple Silicon power. That’s certainly the common regret in computer buying—that if we’d just held off a little longer, we’d have gotten something much better. But recently I’ve been thinking that regret may work the other way this time.

These are the things I’ve been putting together:

Undoubtedly, there are people who will happily trade five of six hours of battery life for a significantly lighter machine. And there may come a time when I’ll be one of them. But not now. Because of Apple’s weird interim Air design, I may have stumbled into the ideal computer for me.

What’s the diff?

This morning’s post by John D. Cook brought up an interesting problem: what’s a good way to check for differences between files when the files consist of just a few (or maybe just one) very long lines?

The problem is that diff, the standard Unix tool for finding differences, tells you which lines are different. This is great for source code files, which are written by human beings and in which the lines tend to be short, but not so much for machine-generated files like JSON, XML, and some HTML files, in which line breaks may be few and far between.

Cook’s solution for comparing long-lined files is to pass them through fold before sending them to diff:

diff <(fold -s -w 20 temp1.txt) <(fold -s -w 20 temp2.txt)

The -s option tells fold to break at space characters instead of in the middle of a word. The -w 20 option tells it to make the new lines no more than 20 characters long. Breaking the text into lines of only 20 characters is overkill, but it certainly is easy to see differences between lines when you have only 20 characters to scan through.

The <() thing is a bit of clever shell scripting known as process substitution. It’s used instead of piping when you have more than one input that needs to be fed to a command.

I was unfamiliar with fold until today. Whenever I’ve needed to reformat a text file to a given line length, I’ve used fmt. What I like about fmt is that it defaults to breaking lines at spaces—no need for the equivalent of the -s option. So I’d do

diff <(fmt temp1.txt) <(fmt temp2.txt)

if I were OK with fmt’s default line length of 75 characters, or

diff <(fmt -20 temp1.txt) <(fmt -20 temp2.txt)

if I wanted to use Cook’s extremely short lines.

But I’d be even more likely to open both files in BBEdit and use its Search▸Find Differences▸Compare Two Front Windows command. That would give me this nice two-pane output:

BBEdit differences

In this example, there’s only one line, but it’s broken into easily digestible pieces by BBEdit’s soft wrapping, and the exact spot at which the difference occurs is highlighted by the darker purple background.1

There is a diffing app called Kaleidoscope that I’ve heard good things about, but I’ve never felt hampered by BBEdit.

  1. In his example text, Cook punned by changing Melville’s “hypos” to “typoes.” Not to be outpunned, I changed it to “typees.”