From TextExpander to Keyboard Maestro… again

After a good bit of thinking, I canceled my TextExpander subscription today. This is not the first time I’ve left TextExpander—I dropped it when Smile first adopted a subscription payment model about five years ago and stayed away even when Smile listened to the complaints and lowered the subscription price.

Eventually, though, I returned. TextExpander was the only realistic snippet solution for iOS and iPadOS, and as I found myself writing more and more on my iPad, I couldn’t live without it. Also, I like making temporary snippets to handle common phrases—like the name of a product or a company—that appear often in my writing as I work on a particular project but will never be used after the project is finished. TextExpander has a very efficient way of adding new snippets.

Things have changed over the past few months. My M1 MacBook Air has brought me back to the Mac in a big way. I no longer write anything longer than a text or an email on my iPad, and I don’t expect that to change. So cross-platform expansion isn’t as important as it once was.1 And although Smile seems to have fixed the crashing problem I was having a month or two ago, I’m still leery of TextExpander’s reliance on a bespoke syncing service.

So I’m back to using Keyboard Maestro as my snippet expansion tool. It works well, and I didn’t have to do too much work to switch over. In a rare display of forethought, I didn’t delete my snippet macros. I had merely disabled them when I started using TextExpander again—now I just had to re-enable them.

Keyboard Maestro snippet groups

Yes, there were some snippets from TextExpander that I’d made in the past few years that needed to be moved over to Keyboard Maestro, but that didn’t take much time. Some were even improved in the translation.

And I decided to tackle the one big advantage TextExpander had over Keyboard Maestro: the ability to make a new snippet quickly. By combining AppleScript with Keyboard Maestro itself, I now have a way to make a KM snippet out of what’s on the clipboard.

For example, let’s say I’m writing a report about products made by Mxyzptlk Industries. To make a snippet for that name, I copy it to the clipboard and invoke my new Make Temporary Snippet from Clipboard macro. That brings up this window,

Make Snippet input window

where I can define the trigger (I chose “;mi”) and adjust the expansion if necessary. After clicking OK, I have a new snippet in my Snippet - Temporary group.

Mxyzptlk snippet

Here’s the macro that does it:

Make Snippet macro

The first step asks the user for the snippet information, prepopulating the expansion field with the contents of the clipboard. The second step does all the real work, running this AppleScript:

 1:  tell application "Keyboard Maestro Engine"
 2:    set trigger to getvariable "snippetTrigger"
 3:    set expansion to getvariable "snippetExpansion"
 4:  end tell
 6:  set triggerXML to "<dict>
 7:  <key>MacroTriggerType</key>
 8:  <string>TypedString</string>
 9:  <key>SimulateDeletes</key>
10:  <true/>
11:  <key>TypedString</key>
12:  <string>" & trigger & "</string>
13:  </dict>"
15:  set expansionXML to "<dict>
16:  <key>Action</key>
17:  <string>ByTyping</string>
18:  <key>MacroActionType</key>
19:  <string>InsertText</string>
20:  <key>TargetApplication</key>
21:  <dict/>
22:  <key>TargetingType</key>
23:  <string>Front</string>
24:  <key>Text</key>
25:  <string>" & expansion & "</string>
26:  </dict>"
28:  tell application "Keyboard Maestro"
29:    tell macro group "Snippet - Temporary"
30:      set m to make new macro with properties {name:expansion}
31:      tell m
32:        make new trigger with properties {xml:triggerXML}
33:        make new action with properties {xml:expansionXML}
34:      end tell
35:    end tell
36:  end tell

Lines 1–4 pull in the values of the variables set during the previous macro step. Lines 6–26 define the XML text that defines what will become the new macro’s trigger and action. Finally, Lines 28–36 create a new macro in the Snippet - Temporary group and define it according to the XML. I took the overall structure of this section of the script from the Keyboard Maestro Wiki.

How did I know the XML format? I created a test macro by hand, exported it, and opened the Test.kmmacros file in BBEdit. From there, it was easy to see the parts that defined the trigger and the action. I did a little editing to accommodate the inclusion of the trigger and expansion variables and pasted the result into the script.

Making a Keyboard Maestro macro that runs an AppleScript that creates a new Keyboard Maestro macro is a lot of fun. More important, though, is that it brings KM up to TE’s level when it comes to making new snippets. Now I’m not making any compromises in using Keyboard Maestro for text expansion.

Update 07/11/2021 8:47 AM
Unsurprisingly, there is prior art in the “quick creation of a Keyboard Maestro snippet” field. This tweet from @DasPretzels led me to two macros that do basically what mine does: this one from Tom and this one from Peter Lewis himself. They both have a significant improvement on mine, in that they start with a Copy step, which eliminates one keystroke. So I added that to the top of my macro and also added a Delete Current System Keyboard step to the end. This leaves the clipboard in the same state it was before the macro was invoked.

  1. Even when I was still writing on my iPad, TextExpander had become less useful. At some point, fill-ins just stopped working, and I had to weaken several snippets to accommodate that loss of functionality.