Markdown footnoting in BBEdit

Two years ago, I wrote a post in which I outlined all the little scripts I’d built to help me write blog posts in BBEdit. I’d been writing posts almost exclusively on my iPad for quite a while, but the M1 MacBook Air I bought in February of 2021 turned me back into a Mac-first guy. I haven’t written a post on my iPad since then.

Anyway, I intended to write a series of posts on my blogging scripts but never got around to it. (The last few years in the Drang household have not been conducive to blogging—don’t ask.) Last night, Jason Snell sent a message calling me out on this. He was looking in particular for a Markdown footnote-making script and thought it was unfair that I’d written about having one without sharing it.

So I sent Jason my script and figured that as long as it was fresh in my head, I’d post it here, too. I realized after sending it to him that it had a limitation that should be fixed. I sent Jason the update, and he found a bug in that, so I had to update the update. I think it’s working fine now, so you can download it.

Here’s how it works. Let’s say you’re writing some Markdown in BBEdit and you want to insert a footnote.1 Footnotes are similar to reference links, except the marker text is preceded by a caret (^). I like my footnote text to go immediately after the paragraph with the footnote marker, so that’s how my script is written. If I have this text,

Text in BBEdit before adding footnote

and I want to insert a footnote after the word “nocturnal” in the first paragraph. I put the insertion point at the end of that word and invoke the Footnote script (which I have bound to ⌃F). Up pops this dialog box, asking for the footnote marker text:

Footnote marker dialog

I type in the marker text—we’ll use “night”—hit the return key, and the text will change to this:

Text in BBEdit after inserting footnote marker

Now [^night] is after “nocturnal” in the first paragraph and there’s a new paragraph that starts with [^night]:. The script leaves the insertion point blinking at the end of this new line, so I can start typing in the footnote text right away.

Note that I use soft wrapping when writing. Linefeed characters come at the ends of paragraphs, not at the ends of every line. This is important in how the script works.

OK, so now that you know how to use it, here’s the AppleScript that does the work.

applescript:
 1:  use AppleScript version "2.4" -- Yosemite (10.10) or later
 2:  use scripting additions
 3:  
 4:  display dialog "Footnote marker" default answer ""
 5:  set fnRef to "[^" & text returned of the result & "]"
 6:  set fnLength to length of fnRef
 7:  
 8:  tell application "BBEdit"
 9:    if length of selection is greater than 0 then
10:      select insertion point after last character of selection
11:    end if
12:    set selection to fnRef
13:    set nextParagraph to find linefeed searching in front document
14:    if found of nextParagraph then
15:      set pEnd to characterOffset of found object of nextParagraph
16:    else
17:      set pEnd to (characterOffset of last character of front document) + 1
18:    end if
19:    set character pEnd of front document to linefeed & linefeed & fnRef & ": " & linefeed
20:    select insertion point before character (pEnd + 2 + fnLength + 2) of front document
21:  end tell

The first two lines were added by Script Debugger, the app I use to write AppleScript. I don’t think they’re critical for this script.

Lines 4–6 handle the dialog. They collect the footnote marker text, assemble the footnote marker itself in fnRef, and save the length of fnRef in fnLength. We’ll use these later to edit the text and place the insertion point.

Lines 9–11 handle the situation in which I start with a word selected instead of having the insertion point after the word. If this is the case, the footnote marker will go after the word.

Line 12 inserts the text of fnRef at the insertion point. This is where the footnote marker will appear in the text. Lines 13–18 figure out where the footnote text is supposed to go by searching for the next linefeed character.2 Under most circumstances—handled by the if clause—the footnote text will go into a paragraph of its own between the current paragraph and the next one. If the current paragraph happens to be at the end of the document so there is no next linefeed—the situation handled by the else clause—the footnote text will go in a new paragraph at the end of the document. In either case, the position is saved in the variable pEnd.

Line 19 then inserts two new linefeeds, the footnote marker (again), a colon, and a space at the pEnd position. Line 20 sets the insertion point after the space, so it’s in position for me to type the footnote text.

I have this script saved as part of a BBEdit package, which is a folder of related scripts, clippings, text filters, and other files, but it can be used as a standalone script. Just save it where you normally keep your BBEdit scripts, and it’ll be available for you to use.

Thanks to Jason for his bug-finding and for reminding me of this hanging thread from 2021. Don’t blame him if it takes a while for me to explain the other parts of my Blogging package.

Blogging Palette


  1. Footnotes are not part of John Gruber’s published version of Markdown, but most other flavors of Markdown have them and use the syntax described here. 

  2. This is why it’s important that I write using soft wrapping. If I used hard wrapping, I’d have to search for two consecutive linefeeds and some of the other logic would have to be changed, too.