# More Markdown reference links in BBEdit

My last post described a set of scripts I use to add new reference links to a Markdown document in BBEdit. This one will show the scripts I use to reference links that are already in the document.

There are two situations in which I need to reference a link that’s already in the file being edited:

• I want a second link to a URL I linked to earlier in the document.
• I added a bunch of links to the file through my ;links TextExpander snippet before I started writing, and now I want to refer to one of them.

Either way, the process followed by the script(s) is the same:

1. Scan the document and collect all the reference links in it.
2. Present the list of links to me and allow me to choose the one I want.

I use a set of scripts to do this.

## Get the reference links and return the chosen one

This is a Python script that’s essentially the same as a script I wrote about a year ago for TextMate. It scans standard input for reference links, presents the list of links to the user through a dropdown menu, and returns the chosen reference through standard output. I call the script getreflink, and here’s its source code:

python:
1:  #!/usr/bin/python
2:  # -*- coding: utf-8 -*-
3:
4:  import sys
5:  import re
6:  from subprocess import *
7:
8:  # Set the environment for calling CocoaDialog.
10:  dialogenv = {'PATH': dialogpath}
11:
12:  # Utility function for shortening text.
13:  def shorten(str, n):
14:    'Truncate str to no more than n chars'
15:    return str if len(str) <= n else str[:n-1] + '…'
16:
17:  # Read in the buffer and collect all the reference links.
19:  refs = re.findall(r'^$([^^$]+)\]: +(https?://)?(.+)\$', text, re.MULTILINE)
20:
22:  choices = [ shorten(x[2], 100) for x in refs ]
23:  chosen = Popen(['CocoaDialog', 'standard-dropdown', '--exit-onchange', '--title', 'Previous links', '--text', 'Choose an existing link', '--items'] + choices, stdout=PIPE, env =dialogenv).communicate()[0].split()
24:
25:  # Print the reference for the chosen link.
26:  if int(chosen[0]) == 1 or int(chosen[0]) == 4:
27:    sys.stdout.write(refs[int(chosen[1])][0])


The list of reference links is constructed in Lines 18-19. When Line 19 finishes, the refs list contains a 3-tuple for each link found. The components of each tuple are

1. the reference, which is whatever’s in the brackets;
2. the URL’s http:// or https:// prefix, if there is one (relative links and internal links won’t have one); and
3. the rest of the URL.

Line 22 then creates a list of the third item of each tuple in refs. The string is truncated to 100 characters if necessary to make it fit better in the dropdown menu presented to the user. I don’t show the http:// part of the URL because it doesn’t really add any information to the list and it takes up space.

Line 23 creates a dialog window with a dropdown menu of all the choices and gets the chosen item from the user. The GUI is handled by CocoaDialog, an application written by Mark Stratman to help programmers add a bit of GUI interaction to their scripts. In the version of this script I wrote for TextMate, I used the copy of CocoaDialog that was bundled with TextMate. Here, I’m assuming the user has installed a copy of CocoaDialog in the system-wide Applications folder. The path to the CocoaDialog binary is given in Line 9, and an environment variable for running CocoaDialog through Python’s subprocess module is set in Line 10.

If the user chooses a link, getreflink returns the reference to that link on standard output. If the user cancels, nothing is returned.

## Running getreflink and inserting Markdown code

The AppleScript, called “Old Reference Link”, is what the user calls, either through the BBEdit scripts menu or through a keyboard shortcut. I use a shortcut of ⌃⌥L.1 Here’s the source code:

applescript:
1:  tell application "BBEdit"
2:    set myText to contents of front document
3:    set myRef to do shell script "~/bin/bbstdin " & quoted form of myText & " | ~/bin/getreflink"
4:
5:    if myRef is not "" then
6:      if length of selection is 0 then
7:        -- Add link with empty text and set the cursor between the brackets.
8:        set curPt to characterOffset of selection
9:        select insertion point before character curPt of front document
10:        set selection to "[][" & myRef & "]"
11:        select insertion point after character curPt of front document
12:
13:      else
14:        -- Turn selected text into link and put cursor after the reference.
15:        add prefix and suffix of selection prefix "[" suffix "]" & "[" & myRef & "]"
16:        select insertion point after last character of selection
17:      end if
18:    end if
19:
20:  end tell


Lines 2-3 use the same trick I used in my earlier post to convert carriage returns to linefeeds and pipe the contents of a BBEdit document to a Unix script. The bbstdin script was described in that post, and I won’t repeat the description here.

Line 5 checks to see if the return value of the call to getreflink was empty. If it was, nothing happens and the AppleScript returns without doing anything to the BBEdit document. If it wasn’t, the Markdown code referring to the chosen link is inserted using the same logic (and the same AppleScript code) as in my previous post:

• If text is selected, the selection is enclosed in brackets and followed by the reference, also in brackets. The cursor is placed after the last bracket.
• If no text is selected, an empty pair of brackets is inserted, followed by the reference in brackets. The cursor is placed between the empty brackets, waiting for the user to type the link text.

## Notes

• This system works better than my similar solution for TextMate, which left the link text and surrounding brackets selected and didn’t reposition the cursor to the best spot for further typing. I was able to improve the code because BBEdit has better control over cursor placement.
• I’ve renamed the AppleScript in my earlier post from “Reference link” to “New Reference Link” to make the distinction between the two clearer (and to comply with the usual capitalization convention for AppleScripts).
• I was remiss in my earlier post for not mentioning Seth Brown’s formd tool, which converts the links in a Markdown document from inline links to reference links and vice versa. It’s a very clever system if you like writing with inline links but reading with reference links. I’m pretty sure Brett Terpstra has a similar tool for doing the inline-to-reference conversion, but I don’t know if his does the reverse.
• There’s probably a better place to put scripts like bbstdin and getreflink than my ~/bin directory, but I’m not yet familiar with BBEdit’s packaging conventions.

1. BBEdit already uses ⌃⌥L to bring up the Language menu from the Status Bar, and old-time BBEdit users may prefer to keep it that way. To me, ⌃⌥L makes more sense for Old Reference Link, so I redefined the Language menu shortcut to ⌃⌥⌘L.