While I wrote these MacOS Quick Actions to scratch my own proverbial itch, I think they could be of use to others, so I’m releasing them as open source.

The actions allow you to calculate the word count, line count, and character count of selected text, to convert a text selection to upper case, lower case, or title case, to do the same to the contents of the clipboard, and to convert the clipboard from rich text to plain text and to trim the contents of the clipboard.

You can download the actions and read the docs on GitHub.

As well as describing what each of the Quick Actions do, the GitHub docs also describe how to install the actions, how to use them, and how to assign keyboard shortcuts to them if desired.

If you’re curious to learn how these Quick Actions work, read on.

Automator Quick Actions

All of these Quick Actions are Automator workflows of type Quick Action.

Screenshot showing Quick Action as new Document Type in Automator

The actions for altering the clipboard are configured not to take any input and to be available in any app:

Screenshot showing workflow taking no input and being available in every app

The actions that alter or measure selected text are configured to take selected text in any app. Those for altering the text are configured to replace the exiting value, and those for measuring are not:

Screenshot showing automation action that takes text input in any app and does not replace it

Screenshot showing automation action that takes text input in any app and does replace it

Displaying Messages

The actions that alter the clipboard or measure the selected text report their actions using the Display Notification Automator Action. The actions that measure the selection use a variable to output the calculated value:

Screenshot showing a Display Notification Automator Action with a variable

Reading Input Text & Outputting Replacement Text

The actions that interact with a text selection receive the selection as the input automatically, so no work is needed to fetch the data. The same holds for the output. If the Output replaces selected text checkbox is ticked the output of the last action in the workflow will replace the text selection automatically.

Interacting with the clipboard is not automatic, but trivial.

Interacting with the Clipboard

The actions that alter the clipboard all have the same basic structure — start by getting the current value of the clipboard using a Get Contents of Clipboard action, alter that value in some way, write the updated value back to the clipboard using a Copy to Clipboard action, and finally let the user know the action has completed:

Screenshot showing automation workflow interacting with the clipboard

Processing the Text

Because JavaScript is more powerful, and because I’m very comfortable writing in that language, I prefer to use JavaScrip rather than AppleScript or shell script when writing code blocks in Automator Workflows. This is why most of the quick actions use the Run JavaScript action to process the text, however, I do use the Run Shell Script action for two of the workflows.

Automatic Conversion to Plain Text

If you enable the log at the bottom of the Automator window (View → Log menu or ⌥⌘L) you’ll see evidence of a extremely useful Automator behaviour. When you add either a Run Shell Script or Run JavaScript action into a work-flow Automator converts all inputs to those actions to plain text strings before the action executes. Thos means we can be guaranteed that the text that arrives into the scripting actions is plain text.

Automator even tries to be clever about the conversion — if one of the inputs is a file rather than rich or plain text, automator will replace it with the path to the file as a string.

String Processing with Shell Script

The Run Shell Script action gives you the choice of which shell to execute the script with, and how to send the input to the shell.

Given Apple’s recent move to deprecate Bash and switch to Zsh I’m now using Zsh in my Automation Workflows.

As for the input, that can either be as command line arguments, or, a STDIN. Most terminal commands are designed to receive information from STDIN, so that’s usually the best solution. The way to think of this input method is that it is as if the output from another command was piped into the command in the Run Shell Script action with the | operator.

Regardless of the input method, the output from a Run Shell Script action is STDOUT.

In this case I used the wc command with -l and -w flags to in the Quick actions for calculating the number of lines and words in a text selection. Since you can pipe text to the wc command (e.g. cat /etc/hosts | wc -l), it can accept input via STDIN. The wc command prints the number it calculated to STDOUT.

So, because wc can take input form STDIN and outputs to STDOUT, the content of the Run Shell Script action simply needs to be the bare command, so wc -l to calculate the number of lines, and wc -w to calculate the number of words.

Screenshot showing Run Shell Script Automator Action

Note that I did not use wc to calculate the character count. Why? Because wc is not sufficiently UTF-8-aware for my tastes. It counts accented characters like é as two characters, and the same with emoji. For that reason I chose to use JavaScript to do the character count.

String Processing with JavaScript

The Run JavaScript Automator Action behaves a little differently to the Run Shell Script action. It’s simpler in the sense that it doesn’t need any configuration (no choosing of shell or input method), but its more complex in the sense that it takes some boiler-plate code to get the action working.

Automator will load all the JavaScript in the action and then execute a function named run(). This means you are free to define any variables and helper functions you wish, but to get the action to actually do anything you must include a run() function.

When Automator executes the run() function it passes the inputs received as an array of strings as the first argument. It passes a hash table with some status and configuration data as the second argument.

Since we know our input will always be the contents of the clipboard or the contents of a text selection we know we’ll always be passed an array of zero or one strings. Why sometimes zero? If the clipboard contains something that can’t be converted to a string then the input will be an empty array.

To measure or alter text we need to convert the input array to a string. I chose to do that by joining the input into a string using no separator:

input.join('').toLowerCase();

If the input is an array of one string that will evaluate to that string, and if the input is an empty array that will evaluate to an empty string.

As a practical example, here is all the code in the Run JavaScript action in the Selection to lower case quick action:

function run(input, parameters) {
	return input.join('').toLowerCase();
}

Using Variables to Store Information

In order to output the number of lines, words, or characters in an alert we need to capture the output of the relevant Run Shell Script or Run JavaScript action and store it in an Automator variable.

Storing the variable is straight forward, simply add a Set Value of Variable action into the workflow directly after the scripting action that produced the output to be saved.

Using the variable is a little less obvious, but just as easy. Be sure the list of variables is visible at the bottom of the Automator window via either the View → Variables menu, or by clicking the very small variables icon at the very bottom of the window. This will show all variables that exist in the workflow. These can be dragged and dropped into other actions. In this case I dragged and dropped the variable holding the relevant count into the text box for the body of the notification in the Show Notification action:

Screenshot showing a variable in use in an Automator workflow