One-off text filtering in BBEdit

After my recent appearance with David and Katie on the Mac Power Users podcast and a Twitter conversation with Eddie Smith, I’ve been thinking about text transformations in BBEdit. In addition to the single-stage text transformations in its Text menu, BBEDit has a couple of ways to perform complex text transformations:

  1. Text Filters (née Unix Filters), which run a script saved in a special Text Filters folder1 and accessed through the Text‣Apple Text Filter submenu.
  2. Text Factories, which are like Unix pipelines in that they allow you to create a single command that sends the text through a series of successive simple transformations. The main difference between Text Factories and pipelines is that a factories are built up graphically, very much like Automator workflows.

    Text Factory actions

Both Filters and Factories take the selected text (or the entire current document if no text is selected), perform the transformation, and replace the text with their output. This is very much in the Unix tradition of command line text tools except that the current selection (or document) takes the place of standard input and standard output.2

Filters require a certain amount of premeditation; you have to create a script and save it to a particular folder. They’re really most useful for transformations you intend to run often. Factories can be more of an ad hoc solution (although they also can be saved in the Text Filters folder for frequent use) and are therefore more suited than Filters are for one-off transformations.

Factories are a great way for people who don’t have much experience programming or using the command line to automate BBEdit’s Text menu commands in powerful ways. If, however, you’re used to using Unix command line tools to build up transformations, you may find the graphical method of creating Factories somewhat slow. There’s often a lot of clicking needed to put together a Factory and choose the necessary options for each step. You may wish for the ability to just type out a pipeline of commands and send the text through it.

And when I say “you,” of course I mean “me.” Although I’m far from a Unix wizard, I do find myself thinking of transformations in terms of command-line utilities rather than Text Factory actions. If only BBEdit would let me just type out a pipeline to run the selected text through. Surprisingly, it’s not all that hard to give it that ability.

Here’s a shell script, called Any Filter, that I have saved in BBEdit’s Text Filters folder. It’s available to me from the Text‣Apply Text Filter submenu and I have it bound to the ⇧⌃⌥⌘F key combination.

bash:
1:  #!/bin/bash
2:  
3:  cmd=`osascript -e 'get text returned of \
4:      (display dialog "Filter command" \
5:        default answer "sort" \
6:        buttons {"Cancel", "Filter"} \
7:        default button "Filter")'`
8:  eval $cmd

As you can see, most of Any Filter is an AppleScript command that displays a dialog box asking for and collecting the command. The final line is just the execution of that command. Because this is saved as a Text Filter, BBEdit knows to feed it the selected text (or the entire current document) and replace that text with Any Filter’s output.

Here’s how Any Filter works in practice. Suppose I have a bunch of text I want to hard wrap to 40 characters per line and then number each line. For reasons I don’t understand, BBEdit’s Text‣Hard Wrap… command isn’t available as a Text Factory action, so we’ll use the fmt command to do our work. First, we select the text.

Selected text before filtering

Then we run Any Filter and enter the fmt command with a width option of 40 and pipe that through the nl command with the options to use a two-digit, zero-padded number and a colon separator.

Any Filter dialog

When we’re done, the original selected text has been replaced with a hard-wrapped and numbered version.

Text after filtering

Is this easier than first running Text‣Hard Wrap… and then Text‣Add/Remove Line Numbers? I suppose that depends on what you’re used to. I find commands like sort and fmt easier to use than their Text menu equivalents because I’ve been using them a long time. They’re what I think of first when I need to filter some text.

Final comments:

  1. vi users are probably smiling. They don’t have to write a script to get this kind of filtering; Bill Joy put it in right from the beginning.
  2. As you can see in the top screenshot, one of the actions available in Text Factories is to call a Unix command to act as a filter. This is nice because it gives you access to all the command line tools from within the graphical Factory building window. Unfortunately, instead of just allowing you to type in the path to the command, BBEdit forces you to click the Choose… button

    Unix Filter Text Factory action

    and then work your way through the usual Mac file picker sheet.

    File picker

    This is tedious unless you know the Go To Folder trick. Whenever the file picker is active, pressing ⇧⌘G brings up a little subsheet that lets you enter the path directly. Tab completion works in this field, so you don’t have to type out the entire path.

    Direct path entry

  3. You can, of course, do a lot of damage with Any Filter, as much damage as you can from within Terminal. Be careful out there.

  1. Where this folder is depends on your setup. By default, the Text Filters folder is at ~/Library/Application Support/BBEdit/Text Filters, but if you’re a Dropbox user, you may have moved it to ~/Dropbox/Application Support/BBEdit/Text Filters as described here

  2. In fact, when you write a script to use as a Text Filter, you write it to accept standard input and emit standard output. BBEdit takes care of moving the text around before and after the script.