« Keyboard Maestro: Adding TextExpander Snippet Expiry Dates | Main | Keyboard Maestro: Date Picker Macro »

Auto Managing TextExpander Temporary Snippets

Any Mac user interested in things such as automation and productivity will no doubt have come across TextExpander, Smile Software's utility that allows you to instantly replace a string of text with a different string of text. This can be used to auto correct typographic errors, insert boiler plate chunks of text or even trigger scripts or fill-in forms that can return more dynamic sets of text. Like so many others who start using it I now find it indispensable.

The expansion snippets that people create fall into two time-based categories. Those that you want to use long term/forever and those that you need for just a little while to get the job done. Today I'd like to share a little Mac automation to help with the latter.

Temporary Snippets

For snippets I only need to use for a short (or known) period I'm going to use the term temporary snippets. They are ones that I might use just while working on a single document, a particular day or perhaps just a particular piece of work. The point being they have a known period for which they should be valid.

One problem is that if like me you build little systems and processes to re-use on work you can want to standardise on how some snippets work. If you have some in place from the last piece of work, when they expand it isn't always easy to spot that the snippet hasn't been updated for the latest piece of work. Also the more temporary snippets you accumulate, the harder it can become to find the right trigger string for your next temporary snippet. Increasing cognitive load is a sure fire way to slow you down and reduce productivity which is exactly the opposite of what TextExpander is intended to do.

Manually Clearing Down Temporary Snippets

When you start making use of temporary snippets in your work then after a while you realise you need to do some housekeeping and clear them out. Now this can be simple at first and you can just have a single temporary group for those snippets (TextExpander allows you to create libraries of related snippets known as snippet groups) and then delete all of the snippets in it periodically. You may then move on to having multiple groups which can make things easier in many respects but perhaps more difficult in others.

Eventually though you may find that you start to have a mixture in regards to when those snippets should be removed. When you can only get away with deleting some of the snippets (and in fact need to delete some) then again it can start to slow you down as you have to review what each snippet does and decide on whether to retain or remove it.

Manual Clear Down is Not Ideal

As you may have guessed already I've added some automation to the process. Trying to decide at the point of clearing down snippets is far from ideal. As well as potentially creating too much of an interruption it also means that you may end up reviewing a particular snippet many times.

The basis of the automation is straight forward. When I define a snippet that I know is going to be temporary I add a date based tag to it to indicate when it ceases to be valid. Adding this information when I first create the snippet is by far the best time to think about this.

The second part of the approach is that I then have my Mac periodically check and remove any temporary snippets that have been set to expire at (or before) that point.

One point to note before going further on this is that this is a Mac based solution. I do use TextExpander on my iOS devices, but there's no time based automation there (yet) that can do the same thing as I'm going to describe below. However I do sync my TextExpander snippets between my Mac and iOS devices via Dropbox so I do still get the benefits.

Setting Snippet Expiry

Snippets are made up of the following items of data:

  1. Content Type - specifies if a snippet is plain text, rich text or a script.
  2. Content - specifies what should be output by TextExpander.
  3. Label - specifies a description for the snippet.
  4. Abbreviation - specifies the trigger string that activates TextExpander and outputs the specified content.

The content type, content and abbreviation are all data items that affect the operation of the snippet. The label however is meta data as it describes what the snippet is rather than defining how it works. Therefore the label is the logical place to add the expiry date.

I use the label field extensively within TextExpander to describe each snippet I create and I imagine that most TextExpander users would do the same. It makes it much quicker and easier to identify the right snippet when you have it well named so if you're not using it already you should probably reconsider.

As a result I decided that I would append expiry dates to the end of the label field. Given that some labels could reasonably be expected to end with a date and not be ones to expire I also added a marker string to indicate the date as being an expiry date. The string I chose for this was "@@", but if you happen to use this in your labels already you'll see that it's trivial to change this to something else.

To make things simple I chose a standard date format so that I didn't have to create a more complex script. I chose to add dates in the ISO 8601; format (yyyy-MM-dd) as this is a standard for applying unambiguous dates and it also appeals to my developer roots.

For example, if I had a temporary snippet that was the name of the project manager on project X (which seems to have a high turn over of project manager's for some reason), and I know the project wraps up on 28 April 2016 I might create a label for the snippet that looks something like this:

Project X - Project Manager @@ 2016-04-29

When writing the script (and testing it) I decided that if I just included the @@ but no date after it then the snippet would be considered very temporary and would be assumed to be expired on the next run of the clean-up script. I run the clean-up on a daily basis (at the start of the day) so if I only need the snippet for a day or less I can just use the @@ to flag it for expiry and clean-up.

The Clean-Up Script

Whilst there are probably other ways of doing the clean-up on the file level I opted for using TextExpander's AppleScript library to do the work for me. It's entirely suited to the task and it's supported by Smile Software so it was a good fit.

The script begins by querying TextExpander for all the snippet groups. It then queries each of the snippets within each group and examines the label to see if it contains "@@". If it does not, the snippet is ignored and left in place. If it does have an "@@" the script examines the content of the label after the "@@" for a date. If no date is found or a date that is equal to or before the current date the snippet will be deleted. If the date is a future date the snippet will again be left in place.

The script below can be copied and pasted into Apple's Script editor (or another AppleScript capable application (e.g. Keyboard Maestro, CodeRunner, Script Debugger)) to run it. Note also that on lines 3-5 there are references to a debug mode.

The debug mode is something I added whilst testing the script and I've left it in so that you can test the script before letting it loose on your own TextExpander library (though you have got it set to create backups haven't you?) When debug mode is enabled (set to true), the script will display a dialog box for snippet deletion instead of actually deleting the snippet. Of course I'd recommend you also have a read through the script to ensure that you are happy with what it will actually do when run on your Mac.

-- Set the separator for the expiry date stamp
set strExpirySeparator to "@@"
-- Set the debug mode
--set bDebug to true
set bDebug to false

tell application "TextExpander"
    -- Enumerate all the TextExpander snippet groups
    set colGroups to groups
    repeat with objGroup in colGroups
        -- Enumerate all snippets in a group
        set colSnippets to snippets of objGroup
        repeat with objSnippet in colSnippets
            -- Check if the snippet's name contains our filter string
            if strExpirySeparator is in name of objSnippet then
                -- If the snippet has the filter text in it's name get the expiry date
                set strSnippetName to name of objSnippet
                set strExpire to my GetExpiry(strSnippetName, strExpirySeparator)
                -- Check the expiry date
                if strExpire = "" then
                    -- Filter test present but no expiry date mark it for deletion
                    set bRemove to true
                    -- Convert expiry date in yyyy-MM-dd format to a date object
                    set dtExpire to my ConvertToDate(strExpire)
                    -- Check the date
                    if dtExpire is greater than (current date) then
                        -- If it is after today mark it to be kept
                        set bRemove to false
                        -- If it is before today or today mark it for deletion
                        set bRemove to true
                    end if
                end if
                -- Delete the snippet if it is expired
                if bRemove then
                    if bDebug is true then
                        display dialog "Delete " & strSnippetName
                        delete objSnippet
                    end if
                end if
            end if
        end repeat
    end repeat
end tell

-- Extract the expiry date from the snippet name
on GetExpiry(p_strSnippetName, p_strDelimiter)
    set AppleScript's text item delimiters to p_strDelimiter
    set listSnippetName to p_strSnippetName's text items
    -- Restore default delimiter
    set AppleScript's text item delimiters to {""}
    set strExpireOn to item 2 of listSnippetName
    -- Loop to remove spaces from the start of the string
    repeat until strExpireOn does not start with " "
        set strExpireOn to text 2 thru -1 of strExpireOn
    end repeat
    -- Loop to remove spaces from the start of the string
    repeat until strExpireOn does not end with " "
        set strExpireOn to text 1 thru -2 of strExpireOn
    end repeat
    return strExpireOn
end GetExpiry

-- Convert yyyy-MM-dd string to a date object
on ConvertToDate(textDate)
    -- Create a date object to populate
    set dateOutput to the current date
    -- Populate the date object
    set the year of dateOutput to (text 1 thru 4 of textDate)
    set the month of dateOutput to (text 6 thru 7 of textDate)
    set the day of dateOutput to (text 9 thru 10 of textDate)
    set the time of dateOutput to 0
    -- Return the date object
    return dateOutput
end ConvertToDate

Scheduling the Clean-up Script

In terms of running the script you could trigger it manually, but why not schedule it to be run. You could use any number of scheduling tools on the Mac. The typical scheduler on the Mac is now launchd (taking over from the ubiquitous cron utility which is still available) and if you aren't comfortable editing plist files directly you can always opt to get something like Lingon or LaunchControl (both paid apps) to help with this.

I'm personally using Keyboard Maestro to manage my scheduled activities and I have it scheduled to run early each morning. In fact I use Keyboard Maestro a little more than that with temporary snippets; in my next post I'm going to share with you how I use the date picker from my previous Keyboard Maestro post to assist me in setting up temporary snippet expiry.


Expiry date based tagging of snippets via labels and a scheduled AppleScript for the clean-up process is how I automate managing the clean-up of my TextExpander temporary snippets. Maybe this process will work for you or it might be that it needs further tailoring to meet your needs? Perhaps tagging scripts for a particular piece of work and then deleting those snippets across multiple groups is more what you need? Whatever the case hopefully this post has given you a good starting point to automating the management of your temporary TextExpander snippets and keeping you in that productivity zone a bit longer.

If you have found this post useful please feel let me know in the comments below, or send me a Tweet.

You may also want to take a look through some of my many other TextExpander posts and the range of snippet groups you can download for free.

PrintView Printer Friendly Version

EmailEmail Article to Friend

Reader Comments

There are no comments for this journal entry. To create a new comment, use the form below.

PostPost a New Comment

Enter your information below to add a new comment.
Author Email (optional):
Author URL (optional):
Some HTML allowed: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <code> <em> <i> <strike> <strong>