AppleScript and BBEdit
Volume Number: 24 (2008)
Issue Number: 08
Column Tag: AppleScript
AppleScript and BBEdit
Extend and enhance your BBEdit experience
with AppleScript
by José R.C. Cruz
Introduction
The BBEdit text editor is the flagship product of Bare Bones Software. It came onto the scene in 1992 as a better alternative over the anemic TeachText. Now, it's a popular editor for writing source code, HTML and XML files, and even regular text files. Its feature set includes support for more than 32K of text, regex search and replace, syntax coloring, and tag palettes. Its Mac OS X version can also supports both AppleScript and shell scripts.
This article shows how you can use AppleScript to enhance your BBEdit setup. First, it provides a peek into BBEdit's scripting dictionary. Next, it introduces the BBEdit Script menu. Then it shows how to attach a script to any of BBEdit's menu items. The article also comes with examples of useful scripts.
The article assumes that you are familiar with the AppleScript language and the Script Editor. Also, its example scripts are all available from the MacTech site at the following URL: ftp.mactech.com/src/mactech/volume24_2008/24.08.sit
The BBEdit Dictionary
The BBEdit scripting dictionary (Figure 1) consists of eight AppleScript suites. The first three suites, (grey) define the methods and properties that most scriptable applications share. The Required suite defines those that all application must have. The Standard suite defines those that are common to most applications. And the Miscellaneous suite defines are those that an application may have.
Figure 1. The BBEdit Scripting Dictionary
The remaining four suites (colored) are unique to BBEdit. Each suite defines the methods and properties needed for a specific task. To use any of these methods or properties, make sure to call them within a tell...end tell block, as follows.
tell application "BBEdit"
call the BBEdit methods and properties here
end tell application "BBEdit"
Covering all the methods and properties in detail is beyond this article's scope. Instead, this article will focus only on methods and properties used by its script examples.
The BBEdit suite
The BBEdit suite (orange) defines the methods and properties common to all tasks. Some will start and control a specific BBEdit feature. Others will access a specific BBEdit object.
For example, BBEdit 8.x displays its text files in two places: in a window or in a drawer (Figure 2). To access the front window, use the text window property.
tell application "BBEdit"
get text window 1
end tell application "BBEdit"
To access the second document in the drawer, e.g. fubar.txt, use the text document property.
tell application "BBEdit"
get text document "fubar.txt"
end tell application "BBEdit"
Next, you can read the text data in one of two ways. To retrieve the entire text, use the contents property.
tell application "BBEdit"
get contents of text window 1
end tell application "BBEdit"
To retrieve only the selected text, use the selection property.
tell application "BBEdit"
get selection of text window 1 as text
end tell application "BBEdit"
Figure 2. The BBEdit display views
Notice that the above example forces the selection property to return its results as text. But if you remove the as text option, the property returns its results as a range of characters.
You can also use the same two properties to replace the data. For example, to replace the entire text to "Lorem ipsum dolor sit amet", use the contents property as follows.
tell application "BBEdit"
set the contents of text window 1 ¬
to "Lorem ipsum dolor sit amet"
end tell application "BBEdit"
To replace only the selected text, use the selection property as follows.
tell application "BBEdit"
set the selection of text window 1 ¬
to "Lorem ipsum dolor sit amet"
end tell application "BBEdit"
Finally, you can display other text data in a separate window. To do so, first use the make method to create a new text window. Then display the text "Lorem ipsum dolor sit amet" as follows.
tell application "BBEdit"
make new text window ¬
with properties {contents:"Lorem ipsum dolor sit amet"}
end tell application "BBEdit"
The Text suite
The Text Suite (green) defines the properties that refer to specific text elements. Use them to read or change parts of the text data.
For example, to retrieve the third word on the front text window, use the word property.
tell application "BBEdit"
get word 3 of text window 1
end tell application "BBEdit"
To read the fifth word of the sixth line on that same window, include a line property.
tell application "BBEdit"
get word 5 of line 6 of text window 1
end tell application "BBEdit"
To read 10 words, starting at the fifth word, from the document foobar.htm, use the word property with a thru keyword.
tell application "BBEdit"
get word 5 thru 15 of text document "foobar.htm"
end tell application "BBEdit"
To change the fifth line of the front text window to "Lorem ipsum dolor", use the line property as follows.
tell application "BBEdit"
set line 5 of text window 1 to "Lorem ipsum dolor"
end tell application "BBEdit"
BBEdit defines words as those text elements that have only letters, numbers, or both. It treats spaces, punctuations, and other characters as delimiters. Consider the following line of sample text.
123 BBEdit property_1 ???
BBEdit sees only four words in the above line: 123, BBEdit, property, and 1. It sees the remaining elements as text delimiters.
The HTML suite
The HTML suite (red) defines the methods and properties used for webrelated tasks. Some work on the HTML data displayed by the front text window. Others work on data stored on a file.
For example, to check the HTML data on the front window, use the balance tags method.
tell application "BBEdit"
balance tags of text window 1
end tell application "BBEdit"
BBEdit returns a TRUE if the HTML data is well formed; otherwise, it returns a FALSE. To check the data on the file foobar.htm, use the check syntax method.
set tPth to path to sites folder from user domain as string
set tPth to tPth & "foobar.htm"
tell application "BBEdit"
check syntax of alias tPth show results true
end tell application "BBEdit"
The above script assumes that the file is in the Sites subdirectory of the home directory. Also, BBEdit displays any errors it finds in a separate window.
The Unix suite
The Unix suite (blue) defines two methods for running a shell script from BBEdit. Make sure that the script starts with a valid #! header. Otherwise, it will either fail to run or terminate with an error.
Suppose you have the shell script foo.sh displayed on a BBEdit text window. To run the script, use to run unix script method.
tell application "BBEdit"
run unix script
end tell application "BBEdit"
To run only the selected portion of that script, set the selection only option to TRUE.
tell application "BBEdit"
run unix script selection only true
end tell application "BBEdit"
To display the results of the script in a separate window, add an output to option as follows.
tell application "BBEdit"
run unix script output to new untitled window
end tell application "BBEdit"
To pass values to the script, first prepare the values as a list. Then pass the list to the script as follows.
set tArg to {"foobar", 24}
tell application "BBEdit"
run unix script output to new untitled window
end tell application "BBEdit"
The script reads the above values as positional parameters.
Now, suppose you have the script foobar.sh stored in your Documents directory. To run that script on the text in the front window, use the run unix filter method.
set tPth to path to documents folder from user domain as string
set tPth to tPth & "foobar.sh"
tell application "BBEdit"
run unix filter alias tPth
end tell application "BBEdit"
In the above example, BBEdit replaces the text with the output of the script. To save the script output in a separate file, set the replacing selection option to FALSE.
set tPth to path to documents folder from user domain as string
set tPth to tPth & "foobar.sh"
tell application "BBEdit"
run unix filter alias tPth replacing selection false
end tell application "BBEdit"
Here, BBEdit saves the output in the file Unix Script Output. It also stores this file in the path ~/Application Support/BBEdit/Unix Support/.
Running Scripts From BBEdit
You can run your AppleScript scripts from BBEdit's Script menu (Figure 3). For your scripts to appear in this menu, make sure to store them in the directory path ~/Application Support/BBEdit/Scripts.
Figure 3. The BBEdit Script menu
You can also remove the Script menu if you have no need for it. To do so, first choose Preferences from the BBEdit menu. Click on the Menus entry to display its preferences panel. Then click on the checkbox Scripts to clear it (Figure 4). You should see the Script menu disappear from the menu bar.
Figure 4. Disabling the Script menu
To restore the Script menu, follow the same steps to display the Menus preferences panel. Then click on the checkbox Scripts to set the option.
Using the menu
The Script menu gives you easy access to various scripting resources. For example, to launch the Apple Script Editor, choose the menu item Open Script Editor from the menu. To view BBEdit's scripting dictionary, choose the menu item Open Scripting Dictionary. To view the contents of the Scripts directory from the Finder, choose Open Scripts Folder.
The Script menu also lets you record your actions without leaving BBEdit. To begin the process, choose the menu item Start Recording. BBEdit will record your actions within and perhaps without.
To end the process, choose the menu item Stop Recording. BBEdit then prompts you for a filename for the script. Also, BBEdit saves the script in its Script directory by default. You can then use the Script Editor to open the script and make any changes you see fit.
Currently, BBEdit does not let you to choose a different script editor such as Script Debugger. This limitation may change in future versions of BBEdit. You can also override the Open Script Editor menu item using a menu action script.
Order of display
The Script menu lists the contents of its directory in alphanumeric order. If its directory has other subdirectories, the menu lists them in the same order as well.
You can, however, change the order in which each script or directory appears on the menu. All you have to do is to add a numeric prefix to their names. Assume, for example, you have the following items in Figure 5. The Script menu displays these items as shown in Figure 6.
Figure 5. Contents of the Script directory
Figure 6. The Script menu before reordering
Now change the name of script foobar to 02)foobar. Also, change the name of subdirectory foobie to 01)foobie. The Script menu will now reorder these items as shown in Figure 7. Notice that the menu excludes the number prefix added to the two names.
Figure 7. The Script menu after reordering
Example menu scripts
The following are three examples of scripts for the BBEdit Script menu. They operate on any text selected on the front text window. For reasons of length, some examples show only the main parts of the script. You can always download the examples from the MacTech website if you want to see the entire scripts.
Feel free to modify these scripts to suit your needs.
The script in Listing 1 is a very simple one. This script encodes the selected text using a ROT13 algorithm. It then displays the encoded result in a separate window. It also sets the title of the output window to that of the source window plus a .rot13 suffix.
Listing 1. The menu script Encode in ROT13
on run
local tTxt, tDoc
tell application "BBEdit"
retrieve the selected text
set tTxt to the selection of text window 1
set tTxt to tTxt as text
retrieve the document title
set tDoc to name of text window 1
end tell application "BBEdit"
validate the selection
if (length of tTxt is 0) then
display alert ¬
"Script Error" message ¬
"You have not selected a text to be encoded" as informational
else
rotate the selection
set tTxt to rotate given target:tTxt
modify the file name
set tDoc to appendRot13 for tDoc
tell application "BBEdit"
create a new text window
make new text window
set name of text window 1 to tDoc
display the rotated text
set the text of text window 1 to tTxt
activate
end tell application "BBEdit"
end if
end run
property kABC : "abcdefghijklmnopqrstuvwxyz"
property kNOP : "nopqrstuvwxyzabcdefghijklm"
Attach the ROT13 suffix
to appendRot13 for aNom
local tOld, tMod, tSuf
set the text item delimiters
set tOld to text item delimiters of AppleScript
set text item delimiters of AppleScript to "."
dissect the filename
set tMod to text item 1 of aNom
set tSuf to text item 2 of aNom
modify the filename
set tMod to tMod & "rot13."
set tMod to tMod & tSuf
restore the text item delimiters
set text item delimiters of AppleScript to tOld
return the modified name
return (tMod)
end appendRot13 for aNom
Subject the text to a ROT13 algorithm
to rotate given target:aTxt
local tPos, tChr, tLst
local tRot
disassemble the target text into its characters
set tRot to ""
set tLst to characters of aTxt
repeat with tChr in tLst
check against the stream of lowercase characters
set tPos to offset of tChr in kABC
if (tPos > 0) then
set tRot to tRot & (character tPos of kNOP)
else
set tRot to tRot & tChr
end if (tPos > 0)
end repeat with tChr in tLst
return the rotated text
return (tRot)
end rotate given target:aTxt
The script in Listing 2 is a bit more complex. First, it reads the selected text from the front text window. It then prompts the user for the recipient's email address and for a message title. Next, the script asks the user to choose which email account to use. Then it uses Apple Mail to prepare a draft email message containing the selected text.
Listing 2. The menu script EMail Selection
on run
local tTxt, tRcv, tSnd
retrieve the selected text
tell application "BBEdit"
set tTxt to the selection of text window 1
set tTxt to tTxt as text
end tell application "BBEdit"
validate the selection
if (length of tTxt is 0) then
display alert ¬
"Script Error" message ¬
"You have not selected a text to be mailed" as informational
else
get the recipient details
set tRcv to askForReceiver()
get the account details
set tSnd to askForAccount()
send the selection
if (tSnd is false) then
DO NOTHING HERE
beep
else
sendMail for tTxt from tSnd to tRcv
end if
end if
end run
Send the text
NOTE:
The following script is a modified version of Apple's sample script
to sendMail for aTxt from aSnd to aRcx
local tMsg, tSub, tRcx
retrieve the following recipient data
set tSub to subject of aRcx
set tRcx to recipient of aRcx
activate Apple Mail
tell application "Mail"
activate
create a new outgoing message
set tMsg to make new outgoing message
set content of tMsg to aTxt & return & return
set subject of tMsg to tSub
set sender of tMsg to item 1 of aSnd
tell tMsg
set visible to true
make new to recipient at end of to recipients ¬
with properties {address:tRcx}
end tell tMsg
end tell application "Mail"
end sendMail for aTxt from aSnd to aRcx
.
.
.
The script in Listing 3 is much more interesting. First, the script counts the number of words and lines in the selected text. Then it counts the number of syllables in each word. Next, it calculates the text's readability index and grade using the FleschKincaid formula. It then displays the results in a dialog window.
To keep things simple, this script approximates the syllable count as the number of vowels in each word.
Listing 3. The menu script FleschKincaid Index
on run
local tStat, tTxt, tWrd
retrieve the selected text
tell application "BBEdit"
set tTxt to the selection of text window 1
set tTxt to tTxt as text
end tell application "BBEdit"
validate the selection
if (length of tTxt is 0) then
display alert ¬
"Script Error" message ¬
"You have not selected a text for analysis" as informational
else
count the number of words in the target text
set tStat to readabilityStats from tTxt
display the results
set tTxt to "Ease Index: "
set tTxt to tTxt & (index of tStat as string)
set tTxt to tTxt & (ASCII character (13))
set tTxt to tTxt & "Grade Level: "
set tTxt to tTxt & (grade of tStat as string)
display dialog tTxt buttons {"OK"} ¬
with title ¬
"FleschKincaid Readability" default button 1 ¬
giving up after 10
end if
end run
Determine the readability statistics of the target text
on readabilityStats from aTxt
local tStat, tWrds, tFKI, tFKG
local tAWL, tASW
initialize the statistics results
set tStat to {sentences:0, wordCount:0, syllables:0, index:0, grade:0}
calculate the following
readability:count:sentences
set sentences of tStat to numberOfLines from aTxt
readability:count:words
set tWrds to numberOfWords from aTxt
set wordCount of tStat to wordCount of tWrds
readability:count syllables
set tWrds to wordList of tWrds
set syllables of tStat to numberOfSyllables from tWrds
try
calculate the following averages
set tAWL to ((wordCount of tStat) / (sentences of tStat))
set tASW to ((syllables of tStat) / (wordCount of tStat))
calculate the FleischKincaid ease index
set tFKI to 206.835 1.015 * tAWL 84.6 * tASW
set index of tStat to tFKI
calculate the FleischKincaid grade level
set tFKG to 0.39 * tAWL + 11.8 * tASW 15.59
set grade of tStat to round (tFKG)
on error
set index of tStat to 0
set grade of tStat to 0
end try
return the statistics results
return (tStat)
end readabilityStats from aTxt
.
.
.
Attaching Scripts To BBEdit
Another way to run scripts on BBEdit is to attach them to a menu item. Known as menu action scripts, they can run before or after the selected menu action. They can enhance the original action or replace it entirely. Menu action scripts are stored in the following directory.
~/Library/Application Support/BBEdit/Menu Scripts.
Also, only version 6.0, or newer, of the BBEdit application supports this type of scripts.
Anatomy of the script
Menu action scripts come in three forms. The first form (Figure 8) has a standard on run handler. When a user selects a menu item, the script runs the handler right after the selection. It then prevents the original menu action from running.
Figure 8. Overriding a menu item action
The second form (Figure 9) has an on menuselect handler. When a user selects a menu item, the script runs the handler before the original action. The handler then returns a Boolean value, which tells the script what to do next. If the handler returns a FALSE, the script allows the original menu action to run. If it returns a TRUE, the script comes to an end.
Figure 9. Preceding a menu item action
The third form (Figure 10) has two handlers: an on menuselect and an on postmenuselect. When a user selects a menu item, the script first runs the on menuselect handler. If this handler returns a TRUE, the script stops, preventing the original menu action to run. But if the handler returns a FALSE, the script lets the original action to run. Then it runs the on postmenuselect handler.
Figure 10. Enclosing a menu item action
To attach a menu action script to a menu item, you need to name the script in a certain way. The following is the syntax you should use for the script's name.
menu•menu_item
The menu string is the name of the menu on the menubar. It can also be the name of the hierarchical menu as well. The menu_item string is the name of menu item itself. For example, to attach a script to the menu item About BBEdit, use the following name for the script.
BBEdit•About BBEdit
To attach a script to the menu item Save As..., use the following name.
File•Save As...
Make sure to separate the two strings with a '•' character. If the menu item contains an ellipsis, make sure to include that as well. Otherwise, BBEdit will not recognize the script.
Example menu action scripts
The following are three examples of menu action scripts for BBEdit. These examples require version 8.x or newer of BBEdit. Feel free to modify these scripts to suit your needs.
Again, for reasons of length, some examples show only the main parts of the script. To see the scripts in their entirety, download the examples from the MacTech website.
The script in Listing 4 overrides the Make Backup Now... menu item in the File menu. First, this script gets the file name and path of the displayed text document. It then prepares the name of the backup tarball using the file's name. Next, it sets the backup path to the directory ~/Documents/Backup. Then it creates the backup tarball in that directory.
Listing 4. The menu action script File•Make Backup Now...
on menuselect(aMenu, anItem)
local tDoc, tBck
retrieve the document information
set tDoc to pathOfDocument()
set tBck to backupName for (dnom of tDoc)
set tDoc to (dpth of tDoc)
backup the document
tell me to storeBackup for tDoc at tBck
end menuselect
Create the tarball backup
on storeBackup for aDoc at aTar
local tNew, tCmd, tBck
prepare the tarball backup
set tBck to pathToBackup()
set tBck to tBck & aTar
does the tarball already exists?
set tNew to pathExists for tBck
prepare the tar command
set tCmd to "tar file=" & (POSIX path of tBck)
set tCmd to tCmd & " label=bbedit_backup"
if (tNew) then
backup:file:create
set tCmd to tCmd & " create "
else
backup:file:update
set tCmd to tCmd & " update "
end if (tNew)
set tCmd to tCmd & (POSIX path of aDoc)
try
execute the backup command
do shell script tCmd
inform the user
if (tNew) then
set tBck to "Created the tarball backup at:" & return ¬
& return & tBck
else
set tBck to "Updated the tarball backup at:" & return ¬
& return & tBck
end if (tNew)
display dialog tBck with title ¬
"Successful Backup" giving up after 5 ¬
buttons {"OK"} default button 1
on error tErr
display alert ¬
"Backup Error" message ¬
(tErr as string) as critical
end try
end storeBackup for aDoc at aTar
.
.
.
The script in Listing 5 overrides the Find Differences... menu item, which is under the Search menu. This script first gets the file path to the displayed text document. It then asks the user to choose a second file to compare against. Next, the script compares the two files using the commandline tool diff. Then it displays the results on a separate text window.
Listing 5. The menu action script Search•Find Differences...
on run
local tDoc, tRef, tDiff
get the frontmost document
set tDoc to frontDocument()
get the document directory
set tDoc to docInfo for tDoc
if (tDoc is not false) then
set tRef to selectTarget for tDoc
set tDiff to getDifferences for tRef against (dpth of tDoc)
display the differences
tell application "BBEdit"
create a new text window
make new text window
set name of text window 1 to "Diff results"
display the results
set the text of text window 1 to tDiff
activate
end tell application "BBEdit"
end if (tDoc is not false)
end run
Get the frontmost document
on frontDocument()
local tDoc, tPth
tell application "BBEdit"
set tDoc to text window 1
set tPth to the file of tDoc
end tell application "BBEdit"
return the retrieval results
return (tPth)
end frontDocument
Display the differences between the two files
on getDifferences for aDoc against aRef
local tCmd, tDiff
prepare the diff command
set tCmd to "diff ignoreallspace"
set tCmd to tCmd & " text"
set tCmd to tCmd & " suppresscommonlines"
set tCmd to tCmd & " " & (POSIX path of aRef)
set tCmd to tCmd & " " & (POSIX path of aDoc)
execute the command
try
do shell script tCmd
set tDiff to result
on error tErr
set tDiff to tErr as string
end try
return the comparison results
return (tDiff)
end getDifferences for aDoc against aRef
.
.
.
The script in Listing 6 overrides the Folder Listing... menu item. This menu item is in the hierarchical menu Insert, which is under the Edit menu. First, the script asks the user to choose a directory. It lists the contents of that directory using the ls commandline tool. Then it displays the results on the front text window.
Listing 6. The menu action script Insert•Folder Listing...
on run
local tDir, tLst
ask for the target directory
set tDir to askForTarget()
get a list of contents from that directory
set tLst to listContents from tDir
display the results
set tDir to "Contents of the directory:" & return & tab ¬
& (POSIX path of tDir)
set tLst to tDir & return & tLst
tell application "BBEdit"
set selection of text window 1 to tLst
end tell application "BBEdit"
end run
Display the contents of the target directory
on listContents from aTgt
local tLst, tCmd
set the shell command
set tCmd to "ls ASl " & (POSIX path of aTgt)
execute the command
try
do shell script tCmd
set tLst to result
on error
set tLst to "Unable to list the contents of the target:" & return
set tLst to tLst & (aTgt as string)
end try
return the retrieval results
return (tLst)
end listContents from aTgt
Ask the user for a target directory
on askForTarget()
local tPth
set the default directory
set tPth to path to documents folder from user domain
prompt the user for a target directory
choose folder ¬
"Select which directory to peruse" default location tPth
set tPth to result
return the selection
return (tPth)
end askForTarget
Final Remarks
BBEdit has a strong and impressive support for AppleScript. It gives you a scripting dictionary with an extensive set of properties and methods. It allows you easy access to various scripts and script resources. It lets you record your actions on demand and save the results to a file. It even lets you attach scripts to specific menu events.
Because of its level of support, BBEdit continues to stand out against other text editors. It is also a prime example of what a scriptable application should be.
Bibliography and References
Borenstein, Philip, Stephen Chernicoff, et al. "Using AppleScripts in BBEdit". BBEdit 8.5 User Manual, pp. 277Ð281. Copyright 19922006. Bare Bones Software Inc.
Apple. AppleScript Language Guide. Copyright 1999. Apple, Inc.