# LaunchBar and LaTeX

When I send a proposal for new work to a client, I do it in the form of a letter addressed to the client and emailed to them as a PDF attachment.1 I write the letter in LaTeX, using TextExpander for the boilerplate and then filling in the job-specific parts. One of the job-specific parts is the name and address of the client, which, in a LaTeX letter, is the second argument to the \begin block:

\begin{letter}{John Appleseed\\
Apple Inc.\\
1 Infinite Loop\\
Cupertino, CA 95014}


In LaTeX, linebreaks in multiline blocks of text have to be preceded by a pair of backslashes. Also, because the ampersand is a special character in LaTeX, it has to be escaped with a backslash. Thus, the firm of Dewey, Cheatham & Howe has to be written in the LaTeX source as Dewey, Cheatham \& Howe.

Over the years, I’ve written several little scripts to format addresses for LaTeX. They’ve all done the job, but they’ve all forced me to either

• move out of my text editor to run the script and then return to paste the result; or
• enter a shell command within the text editor and then select and run it.

Neither is the way I want to work.

When writing on the Mac, I use LaunchBar to look up contacts. I tap ⌘-Space, start typing the contact’s name, and up it pops.

What I want is to be able to run a command at this point that gets the address, puts it in LaTeX form, and pastes it into the letter where the cursor is sitting. This past week, I finally got around to writing a script that does just that.

It uses LaunchBar’s “sending” feature to pass the contact to a script that assembles, formats, and pastes the address—all without leaving LaunchBar. The idea is that after getting the contact as above, I can tap the Tab key to bring up a list of things I can do with it:

Choosing the LaTeXAddress script sends the contact to that script, which then inserts the formatted name and address. No switching out of and back into my text editor, and no messing around with the clipboard.

While I was at it, I decided the script should also handle an address sent to it. That would happen if I select the contact’s address instead of the contact itself:

Will I ever use it this way? It adds an extra step, so probably not. But it was fairly easy to add this extra capability, and it’s usually better to finish off a project when it’s fresh in your mind.

applescript:
1:  use AppleScript version "2.4" -- Yosemite (10.10) or later
3:
4:  (* This script is to be used within LaunchBar. It is sent either a
5:  contact or the address of a contact. When sent a contact, the argument
6:  of handle_string is the full name of the contact as a single line of
7:  text. When sent an address,the argument of handle_string is the street
8:  address as a multi-line block of text with an attn: line that contains
9:  the name.*)
10:
11:  -- The US may show up as any of these names in the country field.
12:  property usNames : {"USA", "US", "United States", "United States of America"}
13:
14:  -- Handle string passed in from LaunchBar
15:  on handle_string(s)
16:    set sList to paragraphs of s
17:
18:    -- Get the plain name and address as a list of text lines.
19:    -- The method called depends on whether a single-line name
20:    -- or multi-line address (with attn:) is passed in.
21:    if (count of sList) is 1 then
23:    else
25:    end if
26:
27:    -- Convert from plain name and address to LaTeX
28:    set blockText to latexify(plainList)
29:
30:    -- Insert it at the cursor
31:    tell application "LaunchBar" to paste in frontmost application blockText
32:  end handle_string
33:
34:
35:  -- Strip the US line part of the address if it's there
36:  on stripUS(aList)
37:    if last item of aList is in usNames then
38:      return items 1 thru -2 of aList
39:    end if
40:  end stripUS
41:
42:
43:  -- Convert from list of lines of plain address to text block
44:  -- formatted according to LaTeX rules
45:  on latexify(aList)
46:    set oldDelims to AppleScript's text item delimiters
47:    set AppleScript's text item delimiters to linefeed
48:
49:    -- Escape the ampersands and end each line except the last with two backslashes
50:    set cmd to "echo " & quoted form of (aList as text) & ¬
51:      "| sed 's/&/\\\\&/g' " & ¬
52:      "| sed '$!s/$/\\\\\\\\/' "
53:    set newBlock to do shell script cmd
54:    return newBlock
55:
56:    set AppleScript's text item delimiters to oldDelims
57:  end latexify
58:
59:
60:  -- Handle single-line name
62:    tell application "Contacts"
63:      --Find the contact with that name
64:      set match to first item of (people whose name is fullname)
65:
66:      -- Get the address and strip the country if US
70:
71:      -- Add the company to the top of the list of lines
72:      set org to organization of match
73:      if org is not missing value then
74:        set beginning of addrList to org
75:      end if
76:
77:      -- Add the name to top of the list of lines
78:      set beginning of addrList to fullname
79:
80:    end tell
81:
84:
85:
88:    -- Strip the country if US
89:    set aList to stripUS(aList)
90:
91:    -- Loop through and find the addressee via the attn: line
92:    -- Keep track of the attn: line number and save in aLine
93:    set lCount to 1
94:    repeat with theLine in aList
95:      if (word 1 of theLine) is "attn" then
96:        set aLength to length of theLine
97:        set addressee to text 7 thru aLength of theLine
98:        set aLine to lCount
99:      end if
100:      set lCount to lCount + 1
101:    end repeat
102:
103:    -- Build a list with everything except the attn: line
104:    set addrList to items 1 thru (aLine - 1) of aList & items (aLine + 1) thru -1 of aList
106:


The handle_string handler, Lines 15–32, is required and is the entry point for the script. Depending on how LaTeXAddress is called, it will get passed either the name of the selected contact—e.g., John Appleseed—or the selected address, which will be a block of text in this form:

Apple, Inc.
attn: John Appleseed
1 Infinite Loop
Cupertino CA 95014
USA


The handle_string function splits its argument into a list of lines (in AppleScript parlance, paragraphs are separated by linefeeds). If there’s only one line, we know that the argument is the full name of the contact, and the addressFromName handler is called. If there’s more than one line, we know the argument is an address block, and the addressFromAddress handler is called. Both of these functions return a list of address lines in the variable plainList, which is then reformatted on Line 28 by the latexify function and then pasted at the cursor position of the current document by the tell application "LaunchBar"… command on Line 31.

The addressFromName function finds the contact from their name (Line 64) and gets the first address listed for that contact (Line 67). It then splits this block of text into lines, stripping off the last line if it’s “USA” or any of the other names in the usNames property defined in Line 12.

The idea here is that the country name is unnecessary for an address in the US. It’s only my Canadian clients that need the country included in the address. The stripping is done by the function stripUS, defined on Lines 36–40.

Lines 72–75 add the company name (if there is one) to the top of the list of lines. Line 78 adds the contact’s name to the top of the list. This list of lines is what addressFromName returns.

The addressFromAddress handler is just a text manipulation function. Line 89 calls stripUS to get rid of the superfluous country name, if present. The rest of the function looks for an attn: line, removes it, and adds the addressee from that line to the top of the list.

The latexify function takes a list of address lines and returns a block of text properly formatted for a LaTeX document. The bulk of the work is done by the pipeline of three sed commands on Lines 51–52. The first one,

sed 's/&/\\\\&/g'


escapes all the ampersands. There are four backslashes in the replacement because this command is processed first by AppleScript, which eats half of the backslashes, and then by the shell, which also eats half of the backslashes. So AppleScript turns four backslashes into two, and then the shell turns two into one. And that’s what we want, one backslash in front of every ampersand. Because there’s no prefix before the s (substitute) command, the command is applied to each line. The g (global) flag at the end of the command tells sed to apply the substitution for every ampersand it finds on a given line.

The second sed command,

sed '$!s/$/\\\\\\\\/'


adds the backslashes required at the ends of the lines. Once again, both AppleScript and the shell consume half of the backslashes, so we need to start with eight to end up with two. The lines to which this substitution is applied are defined by the $! prefix that comes before the s. This is a two-part definition. The $ says to select only the last line, and the ! says to negate (or invert) that selection. The inverse of the last line is every line except the last line, which, if you look at the example up at the top of the post, is exactly what we want.

This system for inserting LaTeX-formatted addresses is more efficient than anything I’ve previously made. It’s possible I can squeeze out another keystroke by building a LaunchBar Action based on this script, but I’m not sure that’s worth the effort. I’ll live with what I have for a while.

1. This may seem old-fashioned to you, but it’s what my clients prefer.