Here’s a quick script that will take the selected tasks in OmniFocus 1.9.4 and put a simple space-indented text representation on the clipboard:
tell application "OmniFocus"
tell first document window of front document
set selectedItems to selected trees of content
if ((count of selectedItems) < 1) then
error "Please first select a task"
end if
set output to ""
repeat with itemItr in selectedItems
set output to output & my printTree(itemItr, 0)
end repeat
end tell
end tell
set the clipboard to output
on printTree(node, indent)
set output to ""
repeat indent times
set output to output & " "
end repeat
set output to output & name of node & (ASCII character 13)
tell application "OmniFocus"
repeat with treeItr in trees of node
set treeItr to treeItr as any
set output to output & my printTree(treeItr, indent + 1)
end repeat
end tell
return output
end printTree
Sometimes I want to swap all instances of “foo” with “bar”. That is, I want every instance of “foo” turned into “bar” and vice-versa.
This may sound odd, but I encounter often it enough that I formed a strategy long ago, three replacement operations:
Replace “foo” with something unique (a sentinal) in the document. I often use a bullet (•) for this.
Replace “bar” with “foo”.
Finally replace the sentinel string with “bar”.
Encountering this again tonight, I decided to finally automate the three-operation replacement:
set uuid to "1DB2FC05-1011-4467-BA2C-A8F3A8B530BC"
set findPasteboard to do shell script "pbpaste -pboard find"
set findPasteboard to findPasteboard & "↔"
display dialog "Swap a↔b" default answer findPasteboard
set swapSet to split of (text returned of result) by "↔"
set term1 to item 1 of swapSet
set term2 to item 2 of swapSet
tell application "BBEdit"
tell text window 1
replace term1 using uuid
replace term2 using term1
replace uuid using term2
end tell
end tell
to split of textToSplit by splitter
set oldTextItemDelimiters to AppleScript's text item delimiters
if the class of splitter is list then
set AppleScript's text item delimiters to splitter
else
set AppleScript's text item delimiters to {splitter}
end if
set theResult to text items of textToSplit
set AppleScript's text item delimiters to oldTextItemDelimiters
return theResult
end split
I now use a hard-coded UUID instead of a bullet for my sentinel, but the idea is the same.
AppleScript’s simple display dialog doesn’t allow two separate text input fields, so I delimit the input with ↔. Hacky.
Here’s a script I wrote to make it easy to switch between tab-based and space-based indention in BBEdit 10:
tell application "BBEdit"
tell text window 1
if expand tabs then
set currentMode to "Spaces (" & tab width & ")"
else
set currentMode to "Tabs"
end if
display dialog "Current Mode: " & currentMode & ".
Enter new mode (blank for tabs):" default answer "4"
set newTabWidth to text returned of result as number
if newTabWidth = 0 then
set expand tabs to false
else
set expand tabs to true
set tab width to newTabWidth
end if
end tell
end tell
Indention control in BBEdit is painful. Whether tabs or spaces are used is in the Edit > Text Options sheet (confusingly named “Auto-expand tabs”) and the amount of spaces inserted in space-indention mode in stuck to the bottom of the Fonts panel (accessible via View > Text Display > Show Fonts).
This script makes it one action to figure out the current mode and modify it if necessary.
I’ve made peace with mostly top-posting my email replies in the name of efficiency.
But there are often times when I want to bottom-post my reply to address a specific section.
I finally broke down a wrote a script to automate the transformation of Mail.app’s top-post format to my preferred bottom-post format.
That is, it turns this:
|
-jwr
On Nov 30, 2010, at 1:13 AM, Grumble Mumble wrote:
> we made some turkey and fixings... time for christmas cookies...
into this:
On Nov 30, 2010, at 1:13 AM, Grumble Mumble wrote:
> we made some turkey and fixings... time for christmas cookies...
|
-jwr
Where I denote the insertion point’s location with the pipe character.
The script assumes your .sig is one line and your reply message window is frontmost:
-- delay a second to give me time to release the
-- FastScript-invocation command key modifiers before
-- the script runs
delay 1
tell application "System Events"
set downArrow to 125
set upArrow to 126
set backspace to (ASCII character 8)
-- select first 4 lines
key code upArrow using command down
repeat 4 times
key code downArrow using shift down
end repeat
-- move those 4 lines to the bottom
keystroke "x" using command down
key code downArrow using command down
keystroke "v" using command down
-- delete 2 trailing lines
keystroke backspace
keystroke backspace
-- delete extra line between "On $DATE $SENDER wrote" and quote
key code upArrow using command down
key code downArrow
keystroke backspace
-- position insertion point to reply area, adding another line
key code downArrow using command down
key code upArrow
key code upArrow
keystroke (ASCII character 13)
end tell
It would be nice if I didn’t trash the clipboard’s contents when moving the top lines to the bottom, but I’m out of patience for this little project.
I’ve bound it to Command-Shift-Option-Down-Arrow using FastScripts.
Update: Apparently there’s a plugin for Mail.app for bottom posting: QuoteFix. Thanks to Caio Chassot and Peter Hosey.
Xcode 3 and Interface Builder 3 don’t like it when you switch files out from underneath them, like what happens when you switch branches in git and mercurial.
Here’s a handy AppleScript that will remember and close all your open Xcode projects and IB documents. Run it again, and all your projects+docs will be reopened, ready again for action.
tell application "System Events"
if exists file ".xcpushpop.plist" of home folder then
-- pop/restore
set plist to property list file "~/.xcpushpop.plist"
set xcList to value of property list item "xc" of plist
set ibList to value of property list item "ib" of plist
repeat with xcItem in xcList
tell application "Xcode" to open xcItem
end repeat
repeat with ibItem in ibList
tell application "Interface Builder" to open ibItem
end repeat
delete file ".xcpushpop.plist" of home folder
else
-- push/save
set openedXcodeProjects to {}
set openedIBDocs to {}
if my isRunning("com.apple.Xcode") then
tell application "Xcode"
repeat with p in project documents
set openedXcodeProjects to openedXcodeProjects & path of p
end repeat
close every project document
end tell
end if
if my isRunning("com.apple.InterfaceBuilder3") then
tell application "Interface Builder"
repeat with d in documents
set openedIBDocs to openedIBDocs & path of d
end repeat
close every document
end tell
end if
set plist to make new property list file with properties {name:"~/.xcpushpop.plist"}
make new property list item at end of property list items of contents of plist ¬
with properties {kind:list, name:"xc", value:openedXcodeProjects}
make new property list item at end of property list items of contents of plist ¬
with properties {kind:list, name:"ib", value:openedIBDocs}
end if
end tell
on isRunning(bundleID)
tell application "System Events"
set procList to every process whose bundle identifier is bundleID
end tell
return procList is not {}
end isRunning
Often I’ll have textual data in columnar (tab-delimited) form.
For serious manipulation, I’ll eventually copy and paste it to a spreadsheet and work with it there, but usually I don’t need to go that far.
I’ve been using this BBEdit AppleScript for years that greatly eases viewing and editing tabular data:
-- Get the input.
tell application "BBEdit"
set input to the selection of text window 1 as text
if input is "" then
set input to the text of window 1
end if
end tell
-- Break the input into rows.
set inputRows to paragraphs of input
if (count of inputRows) is 0 then error "no rows found"
-- Find the longest cell.
set maximumInputCellLength to 0
set oldTextItemDelimiters to AppleScript's text item delimiters
set AppleScript's text item delimiters to {tab}
-- Iterate over the rows.
repeat with inputRow in inputRows
set inputCells to text items of inputRow
-- Iterate over the cells in each row.
repeat with inputCell in inputCells
set inputCellLength to the length of inputCell
if inputCellLength > maximumInputCellLength then ¬
set maximumInputCellLength to inputCellLength
end repeat
end repeat
set AppleScript's text item delimiters to oldTextItemDelimiters
tell application "BBEdit"
set the tab width of text window 1 to maximumInputCellLength + 1
end tell
It turns this:
Into this:
This is the important point: the script doesn’t alter the data itself. Instead, it merely finds the “widest” tab-delimited cell and sets BBEdit’s tab width to match.
Works like a charm.
I burn through a lot of podcasts at the gym, and tend to listen to them out-of-order. With 32 subscriptions, I develop a lot of plaque in the form of listened-to podcast episodes.
This AppleScript deletes all played episodes and their associated files in one fell swoop:
tell application "iTunes"
set librarySource to item 1 of (every source whose kind is library)
set podcastPlaylist to item 1 of (every playlist of librarySource whose special kind is Podcasts)
set playedEpisodes to every file track of podcastPlaylist whose unplayed is false
repeat with playedEpisode in playedEpisodes
set playedEpisodeFile to location of playedEpisode
ignoring application responses
tell application "Finder"
try
delete playedEpisodeFile
end try
end tell
end ignoring
delete playedEpisode
end repeat
--delete playedEpisodes -- this used to work under iTunes 8.2 on Mac OS X 10.5.8... Sigh.
end tell
I guess I’ve never publicly mentioned it, but I use this script for Mail.app extensively:
tell application "Mail"
set nextSelectedMessageID to missing value
set selectedMessages to the selection
if (count of selectedMessages) = 1 then
set selectedMessageID to id of item 1 of selectedMessages
-- Find the selected message's position in the list.
set selectedMessageIndex to missing value
set msgList to every message of message viewer 1
set msgCount to count of msgList
repeat with msgIndex from 1 to msgCount
if id of item msgIndex of msgList = selectedMessageID then
set selectedMessageIndex to msgIndex
exit repeat
end if
end repeat
if selectedMessageIndex = 1 then
-- On the "last" msg: select the one previous
if (count of msgList) > 1 then
set nextSelectedMessageID to id of item 2 of msgList
end if
else
set nextSelectedMessageID to id of item (selectedMessageIndex - 1) of msgList
end if
else
-- TODO handle multiple selection. Somewhat thorny.
end if
repeat with selectedMessage in selectedMessages
set the mailbox of selectedMessage to mailbox "Read"
end repeat
if nextSelectedMessageID is not missing value then
set x to item 1 of (every message of message viewer 1 whose id is nextSelectedMessageID)
set selected messages of message viewer 1 to {x}
end if
end tell
I’ve bound it to ⌘-\ using FastScripts, and the script’s selection-updating heuristic means most of the time I can read, reply (⌘-shift-R, type-type-type, ⌘-shift-D), file the original away and move to the next message (⌘-\) without leaving the keyboard.
Most of the script’s work is figuring out the current selection in composite message viewers (such as “Inbox”) in such a way I can figure out what the logical “next” message is.
About a month back I wrote and posted an AppleScript that makes it easy to blog up a tweet. I wrote:
I haven’t used the script yet, and may never. It’s more an interesting experiment to have it lying around to see if it comes in handy.
Turns out it’s very handy, and I’ve made great use of it.
But it’s been annoying that the script creates a new blog posting each time it’s invoked, so I tweaked it to just inject the quoted tweet into the frontmost document. Enjoy:
property kReturn : ASCII character 13
tell application "Twitterrific"
set t to selection
set tweetScreenName to screen name of t
set tweetUserName to user name of t
set tweetID to id of t
set tweetText to text of t
end tell
tell application "MarsEdit"
set input to selected text in document 1
set output to "[" & ¬
tweetUserName & ¬
" / @" & ¬
tweetScreenName & ¬
"](" & "https://twitter.com/" & ¬
tweetScreenName & ¬
"/status/" & tweetID & "):" & ¬
kReturn & ¬
"> " & tweetText
if length of input > 0 then
set output to kReturn & kReturn & output
end if
set (selected text in document 1) to input & output
end tell
I usually recommend .xcconfig files, but if you don’t want to mess with them, here’s an AppleScript that will Hoseyify all your currently-open Xcode project warnings:
property kHoseyXcodeWarningSettings : {¬
{"GCC_WARN_CHECK_SWITCH_STATEMENTS", "YES"}, ¬
{"GCC_WARN_SHADOW", "YES"}, ¬
{"GCC_WARN_64_TO_32_BIT_CONVERSION", "YES"}, ¬
{"GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED", "YES"}, ¬
{"GCC_WARN_ABOUT_RETURN_TYPE", "YES"}, ¬
{"GCC_WARN_MISSING_PARENTHESES", "YES"}, ¬
{"GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS", "YES"}, ¬
{"GCC_WARN_ABOUT_MISSING_NEWLINE", "YES"}, ¬
{"GCC_WARN_SIGN_COMPARE", "YES"}, ¬
{"GCC_WARN_STRICT_SELECTOR_MATCH", missing value}, ¬
{"GCC_WARN_TYPECHECK_CALLS_TO_PRINTF", "YES"}, ¬
{"GCC_WARN_UNDECLARED_SELECTOR", "YES"}, ¬
{"GCC_WARN_UNUSED_FUNCTION", "YES"}, ¬
{"GCC_WARN_UNUSED_LABEL", "YES"}, ¬
{"GCC_WARN_UNUSED_VALUE", "YES"}, ¬
{"GCC_WARN_UNUSED_VARIABLE", "YES"}, ¬
{"GCC_TREAT_WARNINGS_AS_ERRORS", "YES"}, ¬
{"RUN_CLANG_STATIC_ANALYZER", "YES", "Release"} ¬
}
repeat with setting in kHoseyXcodeWarningSettings
if (count of setting) is 2 then
set setting to setting & missing value
end if
setBuildSetting(item 1 of setting, item 2 of setting, item 3 of setting)
end repeat
on setBuildSetting(buildSettingName, buildSettingValue, buildConfigurationName)
if buildSettingValue is missing value then
tell application "Xcode"
set projectList to every project
repeat with projectIt in projectList
set buildConfigurationList to every build configuration of projectIt
repeat with buildConfigurationIt in buildConfigurationList
if buildConfigurationName is missing value or name of buildConfigurationIt is buildConfigurationName then
delete (every build setting of buildConfigurationIt whose name is buildSettingName)
end if
end repeat
end repeat
end tell
else
tell application "Xcode"
if buildConfigurationName is missing value then
set value of build setting buildSettingName of every build configuration of every project to buildSettingValue
else
set value of build setting buildSettingName of (every build configuration whose name is buildConfigurationName) of every project to buildSettingValue
end if
end tell
end if
end setBuildSetting
You can download it here.
See also: @schwa’s script for enabling Cocoa debugging options.