Markdown reference links in TextMate

Brett Terpstra has released another clever Markdown tool to sit next to his Marked application and Blogsmith Bundle. It’s a sort of Markdown tidy, a filter that runs through a file and turns all your inline links into reference links.

So there’s no misunderstanding, let’s do a quick vocabulary review. Inline links look like this:

This is the [linked text](http://daringfireball.net/projects/markdown)
we use to jump to Gruber's page.

The URL for the link comes right after the linked text. Reference links look like this:

This is the [linked text][1] we use to jump to Gruber's page.

<blah blah blah>

[1]: http://daringfireball.net/projects/markdown

The linked text is followed by a short bit of text, the reference, in brackets. (I generally use numbers, but you can use any text string as the reference.) Elsewhere in the document is that same reference at the start of a line, followed by a colon and the URL. The reference can be anywhere, but those of us with anal retentive tendencies like to put them in a nice neat set at the end of the document.1

Reference links are Markdown’s greatest contribution to document readability. Compare this,

This is the [linked text][1] we use to jump to Gruber's page.

with this,

This is the <a href="http://daringfireball.net/projects/markdown">linked text</a>
we use to jump to Gruber's page.

In HTML, the text we’re actually supposed to be reading gets lost in between the tags. And it’s even worse when the URL is long.

One problem with reference links is all the jumping around you have to do to put them in your document. Put brackets around the linked text, add the reference, go to the bottom of the document, put in the reference and the URL, then go back to spot you started at. You don’t get lost reading reference links, but you can get lost writing them.

That’s the problem Brett’s command solves. You use the easy-to-write inline links as you’re composing, then convert them all into the easy-to-read reference links when its convenient. Best of both worlds.

Brett wrote it as an OS X Service, but late last week he and John Gruber collaborated via Twitter (you can start with this tweet and work backwards) and got it working as a BBEdit command, too.2

Good as Brett’s command is, I have no use for it. A few years ago I wrote a set of TextMate commands that do all the jumping around needed to insert reference links as I write.

When I wrote them back in 2006, I really had no idea how TextMate bundles worked, so they weren’t done in a “bundle friendly” fashion. I suppose I should rewrite them and turn them into a proper bundle someday.

But however poorly structured they are, they work really well and have saved me much time and frustration in using reference links on this blog.

In a similar vein are David Sparks’ TextExpander snippets for handling reference links. They’re very well done, but because they’re implemented in TextExpander, they can’t tell the text editor to put all the references together at the bottom of the document. You have to do that yourself via cut-and-paste. I don’t have the patience for that, but if you’re the kind of person who moves from one text editor to another (as that tramp David does) the portability of TextExpander snippets is a big advantage.

I would think that making a similar set of commands for BBEdit would be pretty easy—easier than what I did for TextMate, since BBEdit has a nice AppleScript library that allows much more control over cursor movement than TextMate does. (I mention this mainly to see if I can goad Clark Goble into writing them. If I ever have to move to BBEdit, it’d be nice if some commands I use every day were already waiting for me.)

Let’s see, who else in the Mac community can I namecheck in this post? How about John Siracusa? In the “Square Bracket Colon Smiley” episode of Hypercritical, he says that reference links in Markdown can be troublesome because you have to scroll down to the bottom of the document to check on your links. One can imagine that this is an acute problem for someone who writes 80,000 word posts, but it’s an annoyance for the rest of us, too.

When you want to use the same reference link more than once in the same document, you either have to remember what reference you gave it (not easy if you’re using auto-generated references as I am) or scroll down to find the reference and scroll back up to insert it at the second link. Neither of these solutions is appealing.

Brett’s Blogsmith Bundle has a Link to Reference command that gathers all the reference links in your document and presents them to you in a little popup menu. Because I use numbers as my references instead of descriptive strings, the command as written is of little use to me, but the idea of putting all the existing reference links in a menu is brilliant.

Blogsmith popup menu for reference links

I don’t know enough Ruby to alter Brett’s command to fit my needs, so I started from scratch with Python. Also, I’ve never really understood Interface Builder, so I decided to use CocoaDialog instead of the dialog bundle to generate the menu of links. My menu isn’t as slick as Brett’s but it does the job.

Popup menu of reference links

When I want to add a new reference link to a URL I’ve previously linked to, I select the text I want to use as the link and press ⌃⌥R. Up comes a little window with a dropdown menu of all the URLs already included in the document. I choose the appropriate URL from the menu and the selected text gets turned into a reference link to that URL. This solves the Siracusa problem of excessive scrolling.

Here’s how the command is set up in TextMate’s Bundle Editor:

Choose reference link command

Here’s the full source of the command:

python:
 1:  #!/usr/bin/python
 2:  # -*- coding: utf-8 -*-
 3:  
 4:  import sys
 5:  import re
 6:  import os
 7:  from subprocess import *
 8:  
 9:  # Set the environment for calling CocoaDialog.
10:  dialogpath = os.environ['TM_SUPPORT_PATH'] + '/bin/CocoaDialog.app/Contents/MacOS/'
11:  dialogenv = {'PATH': dialogpath}
12:  
13:  # Utility function for shortening text.
14:  def shorten(str, n):
15:    'Truncate str to no more than n chars'
16:    return str if len(str) <= n else str[:n-1] + '…'
17:  
18:  # Get the selection.
19:  try:
20:    selection = os.environ['TM_SELECTED_TEXT']
21:  except KeyError:
22:    selection = ''
23:  
24:  # Read in the buffer and collect all the reference links.
25:  text = sys.stdin.read()
26:  refs = re.findall(r'^\[([^^\]]+)\]: +(https?://(.+))$', text, re.MULTILINE)
27:  
28:  # Run the dialog asking for which link to use.
29:  choices = [ shorten(x[2], 100) for x in refs ]
30:  chosen = Popen(['CocoaDialog', 'standard-dropdown', '--exit-onchange' '--title', 'Previous links', '--text', 'Choose an existing link', '--items'] + choices, stdout=PIPE, env =dialogenv).communicate()[0].split()
31:  
32:  # Insert the link.
33:  if int(chosen[0]) == 1:
34:    sys.stdout.write('[%s][%s]' % (selection, refs[int(chosen[1])][0]))
35:  else:
36:    sys.stdout.write(selection)

Lines 9-11 set up the path to the CocoaDialog executable. It’s included as part of TextMate, so there’s no need to install anything new.

Lines 13-16 define a function that truncates a string if it’s longer than a given value. It’s used in Line 29 to keep the URLs in the menu from getting ridiculously long.

Lines 18-22 get the selected text, if any.

Lines 24-26 get the URLs of all the reference links, excluding Markdown footnotes. The beginning of the regex in Line 26 is particularly nasty looking. There’s a caret to signify the beginning of a line, a caret to complement a character set, and a caret because that’s one of the characters I need to exclude. Brackets are used literally, to define a character set, and as the other excluded character within the character set. Yuck.

Lines 28-30 set up the list of menu items from the found URLs and run CocoaDialog.

Lines 32-36 create the link if a menu item was chosen or return the selected text unaltered if not.

If you don’t want to build the command yourself, you can download it. If I ever get around to turning my other reference link commands into a bundle, I’ll add this one to it.


  1. Brett’s command in the Blogsmith bundle puts them all near the top of the document, just under the headers. Same difference. 

  2. Interestingly, the changes needed were very much like the input flexibility I talked about in my Python fileinput post from a few weeks ago. Had they read Lri’s comment on that article, they would’ve known about Ruby’s ARGF right from the start. 


11 Responses to “Markdown reference links in TextMate”

  1. OneEyeBri says:

    I don’t claim to be a “Power Markdown” user to be submitting to this discussion, but I thought I would share how I like to go about reference linking. And yet again…big kudos are in order for Mr. Terpstra.

    If you have not installed Brett’s TabLinks Safari extension, go check it out. It gathers all the URL’s of open tabs and orginizes them in to a list for reference linking. It has the ability to genereate the links in Markdown, HTML or Text. You can fiddle around with the parmeters to set the reference to your choosing.

    So my workflow is as follows.

    • Open a blank Safari window
    • Navagate to all the links I want to reference in individual tabs.
    • Compose my document. I make my reference to the number tab that the link is open to. For example: If leancrew.com is open on the third tab, I will reference [2] in my body reference (TabLinks starts with the first tab at 0).
    • Once the document is complete, I simply go to the TabLinks link render, copy and past it to the end of the document.

    It might not be the fanciest way to go about it, but a quick alt tab gives a nice visual of all your links in Safari. The other advantage is that you save time from having to type out all of the references at the end of the composition phase. Final point, although I have never thought of it until this post, this method could also serve as a nice solution for “quick lookup” when using multipule reference points for a single link.

  2. Lri says:

    End all your reference-linking troubles in 5 lines: ref2inline.rb — Gist

    OneEyeBri, I wrote one of my first AppleScripts to replace TabLinks. It was basically an HTML version of AppleScript for copying Markdown reference links to Safari tabs — Gist

  3. Dr. Drang says:

    Lri,

    I’d like to see a fight between your script and Brett’s.

  4. Clark says:

    Out of curiosity why do you use markdown yourself? I have to confess that I either prefer just using an editor that supports styles or else I just write in pure HTML.

  5. Dr. Drang says:

    I’ll never go back to styled text for reasons described here. I prefer Markdown to other formats because of the speed with which I can write it, its portability between computers and OSes, and the flexibility of output formats.

    I really need to write that last post in my “Text files and me” series.

  6. Brett Terpstra says:

    I see us as two villains in a spy movie, our plans crossing paths in ways that make us admire the other’s craftiness. If we joined forces, we could destroy Bond and take over the world.

  7. Brett Terpstra says:

    And Lri. We should bring Lri along, too.

  8. Dr. Drang says:

    I’m afraid I’d be the dumb partner whose ineptitude allows Bond to get away and foil our plans.

    Could you please write a post showing how to use Interface Builder to interact with TextMate? I know about the screencasts that were done way back when, but they were never very helpful to me. I’d love it if my reference getter could abandon CocoaDialog and use a nicer popup menu like yours.

  9. Clark says:

    Alas, I’d write one for you but I just can’t quite figure out the utility of markdown. I just use MarsEdit and quickly switch between WYSIWYG mode and straight HTML. I often use custom style sheets for various things so I like having the ability to add a class to tags and get exactly what I want.

    I kind of like the mindset of markdown and would definitely prefer it to BBtags or whatever it is that forums like Ars Technica’s use.

    It’s just that I can’t quite figure out why I’d bother for blogging.

    I do have some scripts for doing footnotes proper in MarsEdit that I’ve considered updating. However I’ve been so busy I’ve not done much philosophy blogging which was the only place I really used footnotes much.

  10. Walter Higgins says:

    This function makes it easy to insert reference-style links in Emacs…

    (defun markdown-insert-ref-link (link-url link-ref link-title start end)
      "Insert markup for a reference-style link of the form [selected text][link-ref].
    The link reference is added at the end of the current paragraph.
    [link-ref]: link-url   \"link title\"
    "
      (interactive "sLink URL: \nsLink Ref. (e.g. '1' or 'md'): \nsLink Title: \nr")
      (goto-char start)
      (insert "[")
      (goto-char (+ 1 end))
      (insert "][")
      (insert link-ref)
      (insert "]")
      (forward-paragraph)
      (insert (concat "\n[" link-ref "]: " link-url "\t \"" link-title "\""))
      (goto-char (+ (length link-ref) 4 end))
    
      )
    
  11. Dr. Drang says:

    C’mon, Walter! Using Emacs is cheating; it’s way too powerful!