Markdown footnotes in Drafts

Footnotes in Markdown are pretty easy to just type in directly, and that’s what I did for quite a while when writing posts in Drafts on my iPad. A couple of weeks ago, I decided I wanted to add a bit of automation to avoid retyping the footnote marker. Seemed like an easy task, but it took me three tries to get what I wanted.

First, let’s go over what I mean by “footnotes in Markdown.” As defined by John Gruber, Markdown doesn’t support footnotes, but many popular implementations, like MultiMarkdown and PHP Markdown Extra (which I use to build ANIAT pages) do. I’ve even heard tell that Gruber himself uses an off-brand extension to plain ol’ Markdown to handle footnotes on Daring Fireball.

All of these implementations use a slight variation on Markdown’s reference link syntax. Here’s an example:

The low angle feels better when I'm working while sitting
cross-legged on a bed or floor.[^chicago]

[^chicago]: Twenty-five or -six to four.

The idea is to use an reference string that starts with a caret inside square brackets where you want the footnote marker to appear. Then elsewhere in the text (and it could be anywhere, but I like putting it right after the paragraph with the marker) comes that same marker string followed by a colon and the footnote text. The goal of my automation was to type the marker only once and to format the parts on separate lines.

My first thought was to use a TextExpander snippet with a fill-in. Drafts supports TextExpander and so do many other text editors, so this would be a portable solution. Here’s the snippet:

Footnote TextExpander snippet

The idea here is to work easily in the most common situation, which is when I’m typing along and think “here’s a good spot for a footnote.” After typing the snippet’s abbreviation, I’m swept away to the TextExpander app, where I fill in the part of the marker string that comes between the caret and the closing bracket.

TextExpander fill-in in action

After typing in the marker text and tapping the Done button, I’m taken back to Drafts, where the snippet has been put in place. And here’s where this solution is less than ideal. Moving from Drafts to TextExpander and back takes Drafts out of edit mode—when I return from the round-trip to TextExpander, there’s no cursor blinking in the draft, so I can’t just start typing in the footnote text. I have to tap after the colon to reestablish editing mode and set the cursor where it needs to be to type the footnote text. The need to shift back into editing mode (which will happen with any TextExpander snippet that uses fill-ins) is annoying and made me search for another solution.

My second solution was a two-step Drafts action that used the Prompt and Insert Text step types. The Prompt step asks for the marker,

Prompt for marker

and the Insert Text step uses that marker to insert text in a manner very similar to the TextExpander snippet

Insert Text step

In practice, there were two problems with this solution, both having to do with Drafts’s mode shifting. As with the TextExpander snippet, after the text is inserted, I need to tap after the colon to put Drafts back into editing mode and set the cursor. But even before that, when the Prompt window appears, the text field in it doesn’t have focus, so I have to reach up from the keyboard to tap in it before typing. So this was actually worse than the TextExpander solution.

To get what I really wanted, automation that didn’t take me away from the keyboard to tap the screen, I needed to do some scripting. What I came up with was a single-step Drafts action that works like this:

When I want to insert a footnote, I type the whole marker string—brackets, caret, and all—where the footnote marker should go.

The low angle feels better when I'm working while sitting
cross-legged on a bed or floor.[^chicago]|

(The vertical bar shows the cursor position.)

Then I invoke the Footnote action, which is in my “Blogging” action set and has both a keyboard button with an asterisk glyph and an external keyboard shortcut of ⇧⌘8 (⇧8 is the asterisk). This adds a blank line, a copy of the marker string, a colon, and a space. And it sets the cursor after the space, so I’m ready to type in the footnote text.

The low angle feels better when I'm working while sitting
cross-legged on a bed or floor.[^chicago]

[^chicago]: |

This solution requires a little bit more typing—I have to type the brackets and caret myself—but requires no tapping on the screen. The action is defined this way,

Footnote action

and its script looks like this:

 1:  // Cursor location
 2:  var insertionPoint = editor.getSelectedRange()[0];
 4:  // Get footnote marker from text before cursor
 5:  var nearby = draft.content.substring(insertionPoint - 25, insertionPoint);
 6:  var regex = /(\[\^[^\]]+\])$/;
 7:  var fnMarker = nearby.match(regex)[1];
 9:  // Insert a blank line and the marker,
10:  // ready to add the footnote text.
11:  var footnote = '\n\n' + fnMarker + ': ';
12:  editor.setSelectedText(footnote);
13:  editor.setSelectedRange(insertionPoint + footnote.length, 0);

Line 5 grabs the 25 characters before the cursor and puts that string into the variable nearby.1 Lines 6 and 7 then search that string for the bracket-caret-string-bracket text that defines a Markdown footnote marker and put it into the variable fnMarker.

Lines 11 and 12 insert the blank line and the marker followed by a colon and a space. Line 13 sets the cursor after the space, ready for typing in the footnote text.

This was, clearly, harder to write than the TextExpander and Prompt/Insert solutions, but because it does what I want instead of almost what I want, it was worth it.

  1. I can’t search through all the text before the cursor, because there may be other footnote markers earlier in the post. I chose to look back 25 characters because I don’t expect to ever use a marker string longer than that.