keyboard maestro


10
Feb 12

Text Processing Macros

I have a small arsenal of text processing macros in my Keyboard Maestro bunker. I’ve described some of my macros in the past. There are several that do one specific task but I also try to build more generic macros that ask for input too. All of the following macros work on a text selection, rather than performing a snippet expansion. I use TextExpander for snippet expansion. However, Some have sibling macros that works just on the clipboard (like text cleaning). They have different contexts. Generally I want to modify text I am currently writing. However, when pasting between applications it is very handy to be able clean the clipboard contents before pasting. Use these macros as you see fit.

I have several more macros for text processing. Hopefully I’ll find time to write about them in the future. Feel free to use the comments to share macros that you use.

Search and Filter Actions

Keyboard Maestro comes with actions for filtering text on the clipboard. On the surface the actions seem basic. But they are more than find “this” and replace with “that”. While that is useful, KM includes an option for using Regex matches for matching text. That means I can find generic text and formatting characters linke newlines. Some of the macros below use this functionality.

The Filter Clipboard action has a large number of pre-defined filters. They are self explanatory for the most part.

The Search and Replace action is more dependent on user defined matching and replacing.

Replace Spaces

This is a very flexible macro. It’s function is to replace all spaces in a selection with the user entered character or string. The macro uses the Prompt for Input action to request the replacement characters. After performing a cut operation it filters the clipboard for spaces (the Search box actually contains one space character) and replaces with the value of the user input.

Select some text:

Run the macro:

The string is replaced:

Here’s the macro:

As a bonus, I have a macro for replacing multiple sequential white space characters with just a single space. The “replace with” box contains a single space character. This is a quick way to clean up bad type.

Prepend Macro

This is one of those generic macros. I select some text and trigger the macro. I then get a user input dialog to specify the text I want to use for prepending to the selected lines. The macro then performs a cut action. Once the text is on the clipboard, I use the KM search and replace action. I am using one of the more powerful features of this action. Search with matching by regular expression.

This regular expression matches the beginning of each line. Importantly, I am using the multiline flag so that it finds the beginning of each line rather then just the first line, which is the default. The macro then inserts the selected text.

At the end, I clean up my clipboard. I don’t want any cruft added to my clipboard history so I delete the entry off of the stack.

Here are some use cases:

  1. Convert lines to a bulleted MD list by prepending with “*” or “-”
  2. Convert lines to a numbered list. Just insert “1. ” and MD conversion fixes the numbering later.
  3. Select text and prepend with four spaces to get an MMD code block
  4. Insert “>” to create a quote block
  5. Insert a new line to space out lines

Merge Paragraphs

This macro is also using the regex matching but this time I just match on the newline character and replace with a single space. This works great for converting multiple paragraphs into a single block.

Remove Empty Lines

This is another macro that relies on Regex matching. In this case it removes any blank lines between paragraphs.

Fence Text

This macro also asks for user input to avoid needing multiple macros. The dropdown is populated by using the pipe character “|” in the default value field. The rest of the macro is pretty standard stuff. It simply cuts the selected text and surrounds it with the repeated fence string. It then cleans up the clipboard.

Select some text:

Trigger the macro:

Get the new fenced text:

Here’s the macro:

Cleanup Table

This one is based entirely on Dr.Drang’s Python script for cleaning up a Markdown table. I just made a small modification to the script so it integrates with KM.

The Execute Shell Script action is set to paste the results directly so there is no need to include an additional paste action in the macro.

It takes an ugly Markdown table like this:

|Left align|Right align|Center align|
|:---------|----------:|:----------:|
|This|This|This|
|column|column|column|
|will|will|will|
|be|be|be|
|left|right|center|
|aligned|aligned|aligned|

And converts it to a nice table like this:

| Left align | Right align | Center align |
|:-----------|------------:|:------------:|
| This       |        This |     This     |
| column     |      column |    column    |
| will       |        will |     will     |
| be         |          be |      be      |
| left       |       right |    center    |
| aligned    |     aligned |   aligned    |

Here’s the modified version of Dr.Drang’s script:

#!/usr/bin/python

import sys
import os
pyClip = os.getenv('KMVAR_tempClip')

def just(string, type, n):
    "Justify a string to length n according to type."
   
    if type == '::':
        return string.center(n)
    elif type == '-:':
        return string.rjust(n)
    elif type == ':-':
        return string.ljust(n)
    else:
        return string


def normtable(text):
    "Aligns the vertical bars in a text table."
   
    # Start by turning the text into a list of lines.
    lines = text.splitlines()
    rows = len(lines)
   
    # Figure out the cell formatting.
    # First, find the separator line.
    for i in range(rows):
        if set(lines[i]).issubset('|:.-'):
            formatline = lines[i]
            formatrow = i
            break
   
    # Delete the separator line from the content.
    del lines[formatrow]
   
    # Determine how each column is to be justified.
    formatline = formatline.strip('| ')
    fstrings = formatline.split('|')
    justify = []
    for cell in fstrings:
        ends = cell[0] + cell[-1]
        if ends == '::':
            justify.append('::')
        elif ends == '-:':
            justify.append('-:')
        else:
            justify.append(':-')
   
    # Assume the number of columns in the separator line is the number
    # for the entire table.
    columns = len(justify)
   
    # Extract the content into a matrix.
    content = []
    for line in lines:
        line = line.strip('| ')
        cells = line.split('|')
        # Put exactly one space at each end as "bumpers."
        linecontent = [ ' ' + x.strip() + ' ' for x in cells ]
        content.append(linecontent)
   
    # Append cells to rows that don't have enough.
    rows = len(content)
    for i in range(rows):
        while len(content[i]) < columns:
            content[i].append('')
   
    # Get the width of the content in each column. The minimum width will
    # be 2, because that's the shortest length of a formatting string and
    # because that matches an empty column with "bumper" spaces.
    widths = [2] * columns
    for row in content:
        for i in range(columns):
            widths[i] = max(len(row[i]), widths[i])
   
    # Add whitespace to make all the columns the same width and
    formatted = []
    for row in content:
        formatted.append('|' + '|'.join([ just(s, t, n) for (s, t, n) in zip(row, justify, widths) ]) + '|')
   
    # Recreate the format line with the appropriate column widths.
    formatline = '|' + '|'.join([ s[0] + '-'*(n-2) + s[-1] for (s, n) in zip(justify, widths) ]) + '|'
   
    # Insert the formatline back into the table.
    formatted.insert(formatrow, formatline)
   
    # Return the formatted table.
    return '\n'.join(formatted)
print normtable(pyClip)

Scrubbing Text

I have two macros for stripping text clean of junk. One scrubs the clipboard and the other scrubs the current selection (shown). This macro is pretty self explanatory and uses the built-in KM text cleaning functions.

Triggering Macros

I trigger all of these macros from a single system wide hot key that brings up a palette of macros. I’ve described this in the past.


31
Jan 12

Keyboard Maestro Time Tracker [Link]

A clever macro for time tracking by Rafael Bugajewski at Juicy Cocktail.


24
Jan 12

Keyboard Maestro Folder View Macro

Keyboard Maestro has some granular control over window arrangement. Combine that with the ability to trigger specific menu items, I can create a preset view for just about any folder.

For example, this macro opens my Notes folder and displays it sorted by last modification date. I use a minor “If Condition” trick to turn off the toolbar if it is visible. The “If Condition” looks to see if there is a menu item named “Hide Toolbar” available. If it is, then the macro triggers the menu item. The Macro subsequently repositions the window and sets the Arrange By Date Modified option.

Notes View Macro

 


24
Jan 12

Keyboard Maestro Pause Condition — BBEdit Footnotes

Keyboard Maestro 5 contains several new conditional actions. The “Pause Until Conditions Met” actions is modest sounding but contains a lot of power. Fundamentally, the action will pause the currently running macro and watch for a specific condition to be met. What condition? Well, pretty much anything.

 

Wait For Menu

I’m sure there are some very interesting ways to use this, but here’s a somewhat boring yet powerful macro for entering a footnote in Markdown on BBEdit.

Footnote Macro

 

When the macro is triggered, it asks for a footnote title then sets a BBEdit Jump Mark. After jumping to the bottom of the page and inserting a footnote placeholder, the macro waits. It waits for me to finish typing my footnote. When I hit return, the macro continues where it left off and executes the BBEdit “Jump Back” function which places my cursor right back where it was. Then the footnote link is inserted and I can continue typing.

To be clear, other Keyboard Maestro macros can still be run while the first macro is paused and waiting to continue. The Pause action is not pausing KM but rather just pausing the single macro. I don’t think I would let a macro sit paused for days, but certainly for normal use I have not seen any problems.


14
Jan 12

Keyboard Maestro Macro Toggle

Here’s a quick trick. There’s a KM action to toggle a macro group on and off. For me, this is handy while using macros to override some BBEdit functions. If I ever want to get back to the native functionality in BBEdit, I just toggle my BBEdit macro group.


Macro Toggle

The same macro reenables the BBEdit macro group.


8
Jan 12

Stale Mail Rules

Since migrating to Lion, I’ve noticed several of my Mail rules fail to run. I have a number of rules that tag and move incoming messages. Rather than waste time trying to figure out why the rules do not run properly, I just setup a scheduled Keyboard Maestro macro that runs the rules for me. So far, this seems to solve the problem.


Mail Macro

 

 

Edit: Take out the “Activate Mail” step unless you want to drive yourself crazy.


7
Jan 12

BBEdit Reference Link Macro

Quick Keyboard Maestro macro. This one is for inserting a Markdown reference style link at the end of a document. The magic is that it also insert the link at the current position in the line and maintains the insertion position in the current line. It’s all made possible by BBEdit’s “Set Jump Mark” command.

Here’s the macro:


Link Macro

I have a similar macro for inserting footnotes in Markdown.


6
Jan 12

Simpler Affiliate Links

I have a new saying: “If you want something done right, just pique Dr.Drang’s interest.” There’s been constant improvement to this workflow and it’s interesting to see it evolve.

Here’s a bonus macro that creates Amazon affiliate links.[1] For this one, just copy the product link from Amazon and run the macro to extract the product item and create an affiliate link.[2]


Amazon Macro

Here’s the workflow:

  1. Visit an Amazon product page
  2. Copy the page link
  3. Switch back to a document I want the link inserted in
  4. Run the macro to have the affiliate link pasted in

Here’s the AppleScript:

tell application "Keyboard Maestro Engine"
    try
        set oldDelims to AppleScript's text item delimiters -- save their current state
        set myClip to (process tokens "%Variable%tempClip%")
        if myClip does not contain "/dp/" and myClip does not contain "/product/" then
            return "Did Not Find An Amazon Link"
        else
            if myClip contains "/dp/" then
                set AppleScript's text item delimiters to "/dp/"
            end if
            if myClip contains "/product/" then
                set AppleScript's text item delimiters to "/product/"
            end if
            set tempString to text item 2 of myClip
            set AppleScript's text item delimiters to "/"
            set tempString to text item 1 of tempString
            return tempString
        end if
        set AppleScript's text item delimiters to oldDelims -- restore them
    on error
        set AppleScript's text item delimiters to oldDelims -- restore them in case something went wrong
    end try
end tell

Notes

When I’m logged into Amazon, the product page link has a different structure. The AppleScript needs to determine what kind of link I’ve copied. From what I can tell, Amazon product links have either a path that includes “/product/” or “/dp/” right before the product id. That’s what the AppleScript if-else statements are determining.

This macro also has an example of the Keyboard Maestro flow-control actions. If the AppleScript returns a failure, then KM will display a popup message rather than inserting text.

EDIT: I just realized, after using this macro for so long, that I forgot to include a try-catch block to reset AppleScript’s text delimiters. That’s just dumb.


  1. I’m not posting this to ellicit more work from Dr.Drang and Brett Terpstra. This is working well enough for me. Really guys, thanks for all the work.  ↩

  2. You’ll need to replace my affiliate link with your own, unless you just want to be extra generous to me.  ↩


31
Dec 11

Making Safe URLs

I need to do this often enough, I wrote a macro with some Python for it. The macro converts a url with invalid characters (such as spaces) to a safe encoded url. Importantly, the forward slashes, colons, question marks, equal signs, quotes and hashes that are necessary to pass queries and parameters are untouched by the conversion.

A common example for me is as follows.
Select a URL that has bad actors in it:
http://www.macdrifter.com/wp-content/uploads/2011/12/$$shomer fucking?.jpg

Run the macro and the idealized URL is inserted in its place:
http://www.macdrifter.com/wp-content/uploads/2011/12/%24%24shomer%20fucking%3F.jpg

Here’s the Keyboard Maestro Macro:

Here’s the Python script that does all of the work:

#!/usr/bin/env python
from urlparse import urlparse, urlunparse
import urllib
import os

# Get values from Keyboard Maestro workflow
kmVarClipBoard = os.getenv('KMVAR_tempClip')

# Create a parsed url. See: http://docs.python.org/library/urlparse.html
o = urlparse(kmVarClipBoard)

# Remove unsupported characters. '/' is a safe character
newPath = urllib.quote(o.path, '/')
newParams =  urllib.quote(o.params, '/')

# Create a new tuple with our new quoted path as a replacement
n = o[0:2] + (newPath ,) + o[3:]

# Reassemble the entire url
baseUrl = urlunparse(n)
print baseUrl

Of course, if this is passed something that is not a url, then it will throw an exception. If that’s a concern, then check one or more of the url parse values to make sure there is a domain.


24
Dec 11

What’s So Fun About Fake?

I wrote about the Mac application “Fake”[1] awhile ago. From my very first use, I thought it was an incredibly clever tool. Fake is like a super-charged automator for Webkit. It makes automating interactions with webpages easy and simple. But why would I want to use Fake instead of Automator or curl or even a Python script? Here’s an example that I just started using.

If I want to add an affiliate link for a Mac or iOS app, I need to visit the Apple Link Maker page with my affiliate id as a get-parameter in the link. Then I need to choose iOS or Mac apps and type in the application name. I hit search and get back a list of apps. If I typed the name correctly, then the app I want will be on the top of the list.

Link Maker

applist

I’m not done yet. I want the link, so I click the “App Link” to get a pop-over. I then select the link text and copy it. It’s kind of tedious and therefore, I haven’t used them much. What’s worse, all of the content only exists after the JavaScript has executed and added the content to the page. I can’t just submit a form and get a new page, I have to click the Submit button to generate the new document elements. That’s where most web scraping breaks.

JS Popover

This is a common problem with automating web-interactions. Automator, curl or python’s urllib will not have access to the content I want. That’s because developers are expecting a javascript engine to insert content into the DOM. Without a JS engine, the urllib2, Scrapy or BeautifulSoup functions are not useful.[2] That’s where something like Fake comes in handy.

Hello Fake

With Fake, I can automate the entire process. I build a Fake workflow much like I build an Automator workflow. Drag and drop actions and set variables. Fake has auto-discovery for form fields too. Just ctrl-drag from the action to the location on the page to auto-discover the element id. Fake will automatically determine the proper tag hierarchy to find it in the future.

Fake can also press buttons on the page. Now I’m getting somewhere. It can even extract the content I need from the JavaScript pop-over.

Here’s the workflow:

Fake Workflow

Macro Time

But how can I automate this process? Well, I kind of like Keyboard Maestro for this kind of stuff. Luckily, Fake has good AppleScript support. In my macro, KM pops up a form where I choose an app type[3] and type an app name to query. Next, an AppleScript is executed to launch the Fake workflow.

tell application "Fake"
    activate
    open "Macintosh HD 2:Dropbox:Development:Fake Workflows:getAppLink.fakeworkflow"
    delay 1
    run workflow
    wait until done
    close front document
    quit
end tell

Fake can also run AppleScript so it can also get variables from Keyboard Maestro. Here’s the code from the first Fake action that grabs the input from the form:

 
set kmVarAppLink to ""

tell application "Keyboard Maestro Engine"
    set kmVarAppName to (process tokens "%Variable%AppName%")
    set kmVarAppType to (process tokens "%Variable%AppType%")
end tell
tell application "Fake"
    set variable with name "appName" to kmVarAppName
    set variable with name "appType" to kmVarAppType
end tell

Now fake has all of the information needed to load the page, populate the drop downs, click submit and then load the JavaScript pop-over. Finally, it copies the affiliate link to the clipboard for me.

Fake Macro

The final workflow goes something like this:

  1. cmd-opt-M to open my Markdown palette of macros
  2. Type C to choose the macro for appstore affiliate links
  3. Fill out the popup form and click ok
  4. wait for the alert that the macro is complete
  5. paste the affiliate link

Nice. I’m starting to play with Fake as more than just a way to probe web pages. It has some great potential. I haven’t even touched the more powerful actions that allow custom JS to be run against a page.

I would like to see more options to export a workflow to a stand-alone applet or script. Or perhaps a separate headless engine that could run independent of the application (like Keyboard Maestro). I’d also like integration with 1Password for autofill of passwords. I have a couple other ideas for automation that would require hard-coding login credentials and I’m not a fan of that.


  1. Affiliate Link
  2. There are things like Selenium that provide a JS engine to Python but I find it to be overly difficult and time consuming to use. I’m also not good at Python. So there’s that.
  3. I find it interesting that the form value submitted for iOS apps is “software” and the form value for Mac apps is “macSoftware”

21
Dec 11

Cheat Sheet Macros

Here are a few Keyboard Maestro macros that I find very handy. I call them “cheat sheets” but they are essentially a scratch pad system. This is made possible by the persistent nature of Keyboard Maestro variables. They persistent even through restarts.

The workflow for this system goes something like this:

  1. Browse around and see some text I want to remember
  2. Copy to clipboard and hit opt-cmd-V to bring up my scratch board HUD
  3. Hit “b” to add the content to the cheat sheet

Scratch HUD

  1. Later… hit opt-cmd-V and then “d” to get a pop-up window with the text.

Cheat Sheet

The text can be edited and copied as well. The window can be left open without interfering with other macros. In fact, the window can be left open and the cheat macros used with other content. It’s like little temporary post-it notes.

I run two different macros for adding content to the cheat sheet. One macro appends the clipboard to the top and one replaces the content with the current clipboard.

Cheat Sheet Set

Cheat Sheet Set

And here’s the macro for displaying the text.

Cheat Sheet Display

These macros only work with text. There is no image support. It’s a pretty darn fast way to collect a bunch of text and keep it readily available. The example in the screenshot was while I was working on an AppleScript and I want a quick “snapshot” of the current state.

I have a couple other macros that truly are cheat sheets. One displays the MultiMarkdown tags and another shows some of my less used TextExpander shortcuts. Of course, I keep this stuff in Simplenote too, but it’s much faster to lookup by hitting a couple of macro hotkeys than doing a search in NVAlt.


20
Dec 11

Keyboard Maestro HUD [Link]

Tuesday Tip From Devon Technologies, makers of the excellent DevonThink and DevonAgent. Be sure to follow the link to the forums where there are several tips for using Keyboard Maestro with DevonThink.

 


18
Dec 11

Paste and Return Cursor Position [Macro]

I couldn’t resist. This came up on Twitter and I already had a similar macro running. This Keyboard Maestro macro takes the current clipboard string and pastes it into any application and then simulates the left arrow key to move the cursor back to it’s original position. This macro should be application agnostic since it uses standard keyboard input and basic AppleScript functionality. The same functionality should be possible with TextExpander as well.

Github link to download macro

Enjoy, Eddie.

Paste Macro

 

UPDATE: Here’s a different version that’s much faster. It’s just a bit of cursor finagling through simulating key presses. Probably not as nice as a key binding though.

Github link

 


14
Dec 11

Keyboard Maestro Workflow [Link]

Nice overview of my beloved Keyboard Maestro by Michael Schechter.

 

By Way of Clark’s Tech Blog


1
Dec 11

The Keep ‘em Up Macro

Most people are familiar with the setting a Start Up item on Mac OS X and the new Lion behavior of relaunching apps that were open at the last restart is also great. But I’ve also experienced the problem where I’ve gone away on a trip only to have iTunes crash or shutdown for some reason. That means the AppleTV is no longer able to stream SuperWhy episodes from the media Mac and that’s a problem.

I have a Keyboard Maestro macro the simply checks every 10 minutes and launches specific applications if they are not running.

Keep em up

Macro Detail

Now I could write a script to do the same and then schedule the script through Cron in the terminal, but why hassle. Keyboard Maestro makes it easy. I keep a “Cron” folder of macros that just run at a specific time. It doesn’t get much easier than that.