<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-3164056879958238384</id><updated>2011-11-27T16:24:02.135-08:00</updated><category term='Marked'/><category term='AppleScript'/><category term='BibTeX'/><category term='web development'/><category term='time management'/><category term='types'/><category term='Scala'/><category term='ShutterBug'/><category term='shell'/><category term='python'/><category term='society'/><category term='tips'/><category term='animation'/><category term='TextMate'/><category term='searching'/><category term='Food'/><category term='Mac OS X'/><category term='MarsEdit'/><category term='polymorphism'/><category term='Autocomplete'/><category term='TaskPaper'/><category term='science'/><category term='humor'/><category term='power law'/><category term='divide and conquer'/><category term='W3R'/><category term='PDFView'/><category term='twenty questions'/><category term='BibDesk'/><category term='programming'/><category term='tutorial'/><category term='MacUpdate'/><category term='Gmail'/><category term='ICFP'/><category term='pdfsync'/><category term='music'/><category term='heavy tail'/><category term='Plaxo'/><category term='atheism'/><category term='Java'/><category term='concurrency'/><category term='Standard ML'/><category term='Skim'/><category term='Automator'/><category term='ctags'/><category term='Markdown'/><category term='abstraction'/><category term='TextExtras'/><category term='SubEthaEdit'/><category term='functional programming'/><category term='SEEing LaTeX'/><category term='m4'/><category term='MacSanta'/><category term='SkyBlueCanvas'/><category term='fun'/><category term='blogging'/><category term='LaTeX'/><category term='Google Apps'/><category term='distribution'/><category term='Nash equilibrium'/><title type='text'>Applied Abstraction</title><subtitle type='html'>It is the mark of an educated mind to be able to entertain a thought without accepting it. 
    &lt;br&gt; - Aristotle</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://appliedabstraction.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://appliedabstraction.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Michael Barber</name><uri>http://www.blogger.com/profile/15713879666980145844</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>88</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-3164056879958238384.post-3113259035786410492</id><published>2011-07-20T11:53:00.001-07:00</published><updated>2011-08-02T10:04:37.978-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='blogging'/><title type='text'>Code Highlighting Changes</title><content type='html'>The preceding post marks a change in how code will be highlighted on this blog, now being based on Google's client side code highlighting library. What I'm using follows what Luka Marinko &lt;a href="http://lukabloga.blogspot.com/2008/10/to-test-new-highlighting.html"&gt;describes&lt;/a&gt;. &lt;br /&gt;&lt;br /&gt;Overall, I think the result is decent, but not quite as nice as what I was doing before. But it's so much easier that I'm willing to provisionally accept the new look. &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3164056879958238384-3113259035786410492?l=appliedabstraction.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://appliedabstraction.blogspot.com/feeds/3113259035786410492/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3164056879958238384&amp;postID=3113259035786410492' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/3113259035786410492'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/3113259035786410492'/><link rel='alternate' type='text/html' href='http://appliedabstraction.blogspot.com/2011/07/code-highlighting-changes.html' title='Code Highlighting Changes'/><author><name>Michael Barber</name><uri>http://www.blogger.com/profile/15713879666980145844</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3164056879958238384.post-1142731662383605657</id><published>2011-07-20T11:38:00.001-07:00</published><updated>2011-08-02T10:18:55.642-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='AppleScript'/><category scheme='http://www.blogger.com/atom/ns#' term='SubEthaEdit'/><category scheme='http://www.blogger.com/atom/ns#' term='Marked'/><category scheme='http://www.blogger.com/atom/ns#' term='Markdown'/><title type='text'>Markdown in Marked script</title><content type='html'>I've lately been experimenting with writing in &lt;a href="http://fletcherpenney.net/multimarkdown/"&gt;MultiMarkdown&lt;/a&gt; (MMD) format.  As a whole, it's quite pleasant, but it seems best suited for documents that require some formatting, but not much formal notation. That's not the sort of writing I most often do; LaTeX remains my main writing tool. &lt;br /&gt;&lt;br /&gt;Still, MMD is a nice option at times, and has at least one significant advantage over LaTeX: it is lightweight both in its processing and in its writing, so it is easy to do on any computer. On my Macs, I'm using SubEthaEdit (SEE) to compose and have just purchased &lt;a href="http://markedapp.com/"&gt;Marked&lt;/a&gt; to preview. Marked is quite a nice design, intended to work with any editor (in proper Unix philosophy!) and automatically updating the preview whenever the document is saved (pleasantly reminiscent of &lt;a href="http://www.phys.psu.edu/~collins/software/latexmk-jcc/"&gt;Latexmk&lt;/a&gt;, albeit with a &lt;em&gt;much&lt;/em&gt; easier task). Marked is also inexpensive (three bucks!) through the Mac App Store. Unfortunately, Marked 1.1 seems to be a little buggy, but its author has said that he's already fixed the bugs I reported, with the updated version waiting to finish the review process. &lt;br /&gt;&lt;br /&gt;While already a pretty elegant combination, I present below a script to streamline the combination of SEE and Marked a little more. You'll need to install the &lt;a href="http://subethaedit.net/modes/Markdown.mode.zip"&gt;Markdown mode&lt;/a&gt; for SEE. Show the package contents and save the script below into the Scripts folder within. While you're there, throw away the silly rot13 script that is included in the mode for some inexplicable reason. Reload modes in SEE, and you'll have a command that opens the current (Multi)Markdown document in Marked. &lt;br /&gt;&lt;br /&gt;Amusingly, I inadvertently tried typing several things in this post using Markdown syntax (which Blogger doesn't use). It's very natural!&lt;br /&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;&lt;br /&gt;tell application "SubEthaEdit"&lt;br /&gt;    if not (exists path of front document) then&lt;br /&gt;        error "You have to save the document first"&lt;br /&gt;    end if&lt;br /&gt;    set docpath to the path of the front document&lt;br /&gt;end tell&lt;br /&gt;&lt;br /&gt;set mdFile to POSIX file docpath&lt;br /&gt;ignoring application responses&lt;br /&gt;	tell application "Marked" to open mdFile&lt;br /&gt;end ignoring&lt;br /&gt;&lt;br /&gt;on seescriptsettings()&lt;br /&gt;    return {&lt;br /&gt;        displayName:"Preview with Marked", &lt;br /&gt;        shortDisplayName:"Preview", &lt;br /&gt;        keyboardShortcut:"@O", &lt;br /&gt;        toolbarIcon:"ToolbarIconRun", &lt;br /&gt;        inDefaultToolbar:"yes", &lt;br /&gt;        toolbarTooltip:"Preview current document with Marked", &lt;br /&gt;        inContextMenu:"no"&lt;br /&gt;    }&lt;br /&gt;end seescriptsettings&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Edit:&lt;/strong&gt; Made a minor change to the script. For reasons not entirely clear to me, the original form would hang on some files (naturally, none I tested before posting about it!), with SubEthaEdit waiting for a response from Marked. The &lt;code&gt;ignoring application responses&lt;/code&gt; takes care of that.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3164056879958238384-1142731662383605657?l=appliedabstraction.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://appliedabstraction.blogspot.com/feeds/1142731662383605657/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3164056879958238384&amp;postID=1142731662383605657' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/1142731662383605657'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/1142731662383605657'/><link rel='alternate' type='text/html' href='http://appliedabstraction.blogspot.com/2011/07/markdown-in-marked-script.html' title='Markdown in Marked script'/><author><name>Michael Barber</name><uri>http://www.blogger.com/profile/15713879666980145844</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3164056879958238384.post-4215516086881657876</id><published>2010-03-06T10:26:00.001-08:00</published><updated>2010-03-06T10:31:27.764-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='m4'/><category scheme='http://www.blogger.com/atom/ns#' term='AppleScript'/><category scheme='http://www.blogger.com/atom/ns#' term='SubEthaEdit'/><category scheme='http://www.blogger.com/atom/ns#' term='shell'/><title type='text'>Modeless Scripts for SubEthaEdit</title><content type='html'>&lt;a href="http://www.subethaedit.net"&gt;SubEthaEdit&lt;/a&gt; (SEE) supports mode-dependent extensions to its functionality. The mechanism for this is the embedding of AppleScripts into the mode. This lets, for example, Python documents have a &lt;em&gt;Check Syntax&lt;/em&gt; command differently implemented from the identically named &lt;em&gt;Check Syntax&lt;/em&gt;  command for Lua documents. &lt;br /&gt;&lt;br /&gt;All well and good, but there are tasks that are of interest across most or all modes. A prime example for a programmer's editor like SEE is commenting out lines. The basics are the same regardless of language: the line needs to begin with a specific string to indicate a comment. But the specifics do matter: Python needs hashes &lt;code&gt;#&lt;/code&gt; for comments, Lua needs a double hyphen &lt;code&gt;--&lt;/code&gt;, and so on. &lt;br /&gt;&lt;br /&gt;We'd thus like to have scripts that are &lt;em&gt;modeless&lt;/em&gt;, present in any mode, but that are &lt;em&gt;customizable&lt;/em&gt;, appropriate to any mode. Such an AppleScript needs to go into the global scripts menu for SEE, but still allow each mode to define how the behavior of the script should be implemented. &lt;br /&gt;&lt;br /&gt;Here's how to do it. We use an AppleScript to capture the basics of a given pattern, such as determining which  lines should be commented out. The AppleScript then calls a shell script specified for the current mode. &lt;br /&gt;&lt;br /&gt;All the components for this task have been presented previously on this blog, with &lt;a href="http://appliedabstraction.blogspot.com/2008/04/code-indentation-in-subethaedit.html"&gt;code indentation&lt;/a&gt; exemplifying the approach. I'll use &lt;a href="http://appliedabstraction.blogspot.com/2008/01/seeing-latex-19-subethaedittools-or.html"&gt;SubEthaEditTools&lt;/a&gt; to implement the AppleScripts. The mode-specific customization is done using a plist of &lt;a href="http://appliedabstraction.blogspot.com/2007/10/seeing-latex-12-setting-environment.html"&gt;environment&lt;/a&gt; &lt;a href="http://appliedabstraction.blogspot.com/2007/10/seeing-latex-13-accessing-environment.html"&gt;variables&lt;/a&gt;. Particular tasks are done by writing an AppleScript that calls a shell command stored in an environment variable in the plist; the use of SubEthaEditTools makes these scripts quite brief.&lt;br /&gt;&lt;br /&gt;Opening the plist is done with this script:&lt;pre style="color:#000000; background-color:#fff5ee; font-size:10pt; font-family:'Courier New';"&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;if not&lt;/span&gt; &lt;span style="color:#000069"&gt;documentIsAvailable&lt;/span&gt;() &lt;span style="color:#2e8b57; font-weight:bold"&gt;then&lt;/span&gt;&lt;br /&gt;	&lt;span style="color:#2e8b57; font-weight:bold"&gt;return&lt;/span&gt;&lt;br /&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color:#000069"&gt;openEnvironmentSettings&lt;/span&gt;()&lt;br /&gt;&lt;br /&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;on&lt;/span&gt; &lt;span style="color:#000069"&gt;seescriptsettings&lt;/span&gt;()&lt;br /&gt;	&lt;span style="color:#2e8b57; font-weight:bold"&gt;return&lt;/span&gt; {displayName:&lt;span style="color:#ff1493"&gt;&amp;quot;Customize Mode...&amp;quot;&lt;/span&gt;, shortDisplayName:&lt;span style="color:#ff1493"&gt;&amp;quot;Environment&amp;quot;&lt;/span&gt;, inContextMenu:&lt;span style="color:#ff1493"&gt;&amp;quot;no&amp;quot;&lt;/span&gt;}&lt;br /&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;end&lt;/span&gt; seescriptsettings&lt;br /&gt;&lt;br /&gt;&lt;span style="color:#000069"&gt;include&lt;/span&gt;(`SubEthaEditTools.applescript')&lt;br /&gt;&lt;/pre&gt;The &lt;code&gt;include&lt;/code&gt; command is not part of AppleScript, it is an &lt;code&gt;m4&lt;/code&gt; macro used to &lt;a href="http://appliedabstraction.blogspot.com/2008/01/what-role-should-script-editor-play-in.html"&gt;modularize the scripts&lt;/a&gt;. The logic is simple: make sure there is a document available, then use its mode to open the mode-specific environment settings.&lt;br /&gt;&lt;br /&gt;For a particular function we include the common features and call out to the shell to do the rest. For adding or removing line-oriented comments, I used:&lt;pre style="color:#000000; background-color:#fff5ee; font-size:10pt; font-family:'Courier New';"&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;if not&lt;/span&gt; &lt;span style="color:#000069"&gt;documentIsAvailable&lt;/span&gt;() &lt;span style="color:#2e8b57; font-weight:bold"&gt;then&lt;/span&gt;&lt;br /&gt;	&lt;span style="color:#2e8b57; font-weight:bold"&gt;return&lt;/span&gt;&lt;br /&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;if&lt;/span&gt; (modeSetting &lt;span style="color:#696969; font-weight:bold"&gt;for&lt;/span&gt; &lt;span style="color:#ff1493"&gt;&amp;quot;COMMENTER&amp;quot;&lt;/span&gt;) &lt;span style="color:#2e8b57; font-weight:bold"&gt;is&lt;/span&gt; &lt;span style="color:#696969; font-weight:bold"&gt;missing value&lt;/span&gt; &lt;span style="color:#2e8b57; font-weight:bold"&gt;then&lt;/span&gt;&lt;br /&gt;	&lt;span style="color:#696969; font-weight:bold"&gt;display&lt;/span&gt; alert &lt;span style="color:#ff1493"&gt;&amp;quot;Commenting not available.&amp;quot;&lt;/span&gt; message &lt;span style="color:#ff1493"&gt;&amp;quot;You need to define COMMENTER for the mode.&amp;quot;&lt;/span&gt;&lt;br /&gt;	&lt;span style="color:#2e8b57; font-weight:bold"&gt;return&lt;/span&gt;&lt;br /&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color:#000069"&gt;completeSelectedLines&lt;/span&gt;()&lt;br /&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;set&lt;/span&gt; outText &lt;span style="color:#2e8b57; font-weight:bold"&gt;to&lt;/span&gt; shellTransform &lt;span style="color:#2e8b57; font-weight:bold"&gt;of&lt;/span&gt; &lt;span style="color:#000069"&gt;selectionText&lt;/span&gt;() &lt;span style="color:#696969; font-weight:bold"&gt;for&lt;/span&gt; &lt;span style="color:#000069"&gt;modeEnvironment&lt;/span&gt;() through &lt;span style="color:#ff1493"&gt;&amp;quot;eval $COMMENTER&amp;quot;&lt;/span&gt; &lt;span style="color:#2e8b57; font-weight:bold"&gt;without&lt;/span&gt; alteringLineEndings&lt;br /&gt;setSelectionText &lt;span style="color:#2e8b57; font-weight:bold"&gt;to&lt;/span&gt; outText&lt;br /&gt;&lt;br /&gt;&lt;span style="color:#2929cc"&gt;-- SubEthaEdit settings&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;on&lt;/span&gt; &lt;span style="color:#000069"&gt;seescriptsettings&lt;/span&gt;()&lt;br /&gt;    {displayName:&lt;span style="color:#ff1493"&gt;&amp;quot;Un/Comment Selected Lines&amp;quot;&lt;/span&gt;, keyboardShortcut:&lt;span style="color:#ff1493"&gt;&amp;quot;&amp;#64;/&amp;quot;&lt;/span&gt;, inContextMenu:&lt;span style="color:#ff1493"&gt;&amp;quot;yes&amp;quot;&lt;/span&gt;}&lt;br /&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;end&lt;/span&gt; seescriptsettings&lt;br /&gt;&lt;br /&gt;&lt;span style="color:#000069"&gt;include&lt;/span&gt;(`SubEthaEditTools.applescript')&lt;br /&gt;&lt;/pre&gt;Again, the logic is simple: get the &lt;code&gt;COMMENTER&lt;/code&gt; shell command from the mode-specific environment for the front current document, and use it to transform the selected lines. &lt;br /&gt;&lt;br /&gt;So what should the shell command be? One possibility is the comment script presented in the &lt;a href="http://appliedabstraction.blogspot.com/2010/02/better-comment-script.html"&gt;preceding post&lt;/a&gt;. Some examples are given in that post: use those as the value in the environment variable plist, with COMMENTER as the key. There is no default comment method, so it's necessary to provide values for each of the modes you use. In practice, this isn't bad, since you can often just copy and paste between modes with minimal or no changes. &lt;br /&gt;&lt;br /&gt;It's just as easy to define a script for block comments, such as those used in C or SML. I'll omit the details. &lt;br /&gt;&lt;br /&gt;The AppleScripts and some supporting scripts are available for &lt;a href="http://www.box.net/shared/ns6jeny3ny"&gt;download&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3164056879958238384-4215516086881657876?l=appliedabstraction.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://appliedabstraction.blogspot.com/feeds/4215516086881657876/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3164056879958238384&amp;postID=4215516086881657876' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/4215516086881657876'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/4215516086881657876'/><link rel='alternate' type='text/html' href='http://appliedabstraction.blogspot.com/2010/03/modeless-scripts-for-subethaedit.html' title='Modeless Scripts for SubEthaEdit'/><author><name>Michael Barber</name><uri>http://www.blogger.com/profile/15713879666980145844</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3164056879958238384.post-551589279712947384</id><published>2010-02-28T04:41:00.001-08:00</published><updated>2010-04-20T03:29:20.492-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='SEEing LaTeX'/><category scheme='http://www.blogger.com/atom/ns#' term='SubEthaEdit'/><category scheme='http://www.blogger.com/atom/ns#' term='shell'/><title type='text'>A Better Comment Script</title><content type='html'>A couple of years ago, I &lt;a href="http://appliedabstraction.blogspot.com/2007/12/seeing-latex-15-comments-on-comments.html"&gt;presented&lt;/a&gt; a shell script for commenting out lines, for use in a &lt;a href="http://appliedabstraction.blogspot.com/search/label/SEEing%20LaTeX"&gt;LaTeX mode&lt;/a&gt; for SubEthaEdit. The script is an improvement over the AppleScript approach used in other SubEthaEdit modes, but does something I don't really like: it always inserts or removes comments at the beginning of the lines, rather than at an appropriate indentation level. &lt;br /&gt;&lt;br /&gt;Below, I present &lt;code&gt;line-comment&lt;/code&gt;, an awk script that handles line-oriented comments, taking indentation into account. Lines to comment or uncomment are read from Standard Input, and the processed lines are written to Standard Output. The script uses the current commenting of the lines to determine whether to comment or uncomment. &lt;br /&gt;&lt;br /&gt;There are two options that can be set. First, there is the &lt;code&gt;TabWidth&lt;/code&gt;, which defines an indentation level; this defaults to the (basically useless) Unix standard of an eight-space tab. You'll almost always want to set this, even if you're using tabs, not spaces, for indentation. Second, there is the &lt;code&gt;CommentString&lt;/code&gt;, whose meaning should be obvious; this defaults to the hash character &lt;code&gt;#&lt;/code&gt; common to many programming languages. &lt;br /&gt;&lt;br /&gt;As an example, a nice choice for Python could be &lt;code&gt;line-comment TabWidth=4&lt;/code&gt;, while for Scala &lt;code&gt;line-comment TabWidth=2 CommentString="//"&lt;/code&gt; would be more suitable. &lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Update:&lt;/strong&gt; If the script doesn't seem to work, try setting the environment variable &lt;code&gt;COMMAND_MODE=unix2003&lt;/code&gt;. This is relevant if you want to &lt;a href="http://appliedabstraction.blogspot.com/2010/03/modeless-scripts-for-subethaedit.html"&gt;call it from SubEthaEdit&lt;/a&gt;: SEE uses &lt;code&gt;COMMAND_MODE=legacy&lt;/code&gt;, which can cause the regular expressions to fail to match. &lt;br /&gt;&lt;br /&gt;The script is unchanged:&lt;pre style="color:#000000; background-color:#fff5ee; font-size:10pt; font-family:'Courier New';"&gt;&lt;span style="color:#2929cc"&gt;#! /usr/bin/awk -f&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color:#696969; font-weight:bold"&gt;function&lt;/span&gt; trimIndent&lt;span style="color:#676767"&gt;(&lt;/span&gt;text&lt;span style="color:#676767"&gt;,&lt;/span&gt; indRE&lt;span style="color:#676767"&gt;,&lt;/span&gt;   n&lt;span style="color:#676767"&gt;,&lt;/span&gt; tokens&lt;span style="color:#676767"&gt;) {&lt;/span&gt;&lt;br /&gt;  &lt;span style="color:#2929cc"&gt;# Returns the text with the indentation removed. Sets&lt;/span&gt;&lt;br /&gt;  &lt;span style="color:#2929cc"&gt;# global variable IndentLevel to show how many&lt;/span&gt;&lt;br /&gt;  &lt;span style="color:#2929cc"&gt;# indentation levels were removed.&lt;/span&gt;&lt;br /&gt;  n &lt;span style="color:#676767"&gt;=&lt;/span&gt; &lt;span style="color:#2e8b57; font-weight:bold"&gt;split&lt;/span&gt;&lt;span style="color:#676767"&gt;(&lt;/span&gt;text&lt;span style="color:#676767"&gt;,&lt;/span&gt; tokens&lt;span style="color:#676767"&gt;,&lt;/span&gt; indRE&lt;span style="color:#676767"&gt;)&lt;/span&gt;&lt;br /&gt;  &lt;span style="color:#2e8b57; font-weight:bold"&gt;if&lt;/span&gt; &lt;span style="color:#676767"&gt;(&lt;/span&gt;n &lt;span style="color:#676767"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#ff1493"&gt;1&lt;/span&gt;&lt;span style="color:#676767"&gt;) {&lt;/span&gt;&lt;br /&gt;    IndentLevel &lt;span style="color:#676767"&gt;=&lt;/span&gt; n&lt;span style="color:#676767"&gt;-&lt;/span&gt;&lt;span style="color:#ff1493"&gt;1&lt;/span&gt;&lt;br /&gt;    rest &lt;span style="color:#676767"&gt;=&lt;/span&gt; tokens&lt;span style="color:#676767"&gt;[&lt;/span&gt;n&lt;span style="color:#676767"&gt;]&lt;/span&gt;&lt;br /&gt;  &lt;span style="color:#676767"&gt;}&lt;/span&gt; &lt;span style="color:#2e8b57; font-weight:bold"&gt;else&lt;/span&gt; &lt;span style="color:#676767"&gt;{&lt;/span&gt;&lt;br /&gt;    IndentLevel &lt;span style="color:#676767"&gt;=&lt;/span&gt; &lt;span style="color:#ff1493"&gt;0&lt;/span&gt;&lt;br /&gt;    rest &lt;span style="color:#676767"&gt;=&lt;/span&gt; text&lt;br /&gt;  &lt;span style="color:#676767"&gt;}&lt;/span&gt;&lt;br /&gt;  &lt;span style="color:#2e8b57; font-weight:bold"&gt;return&lt;/span&gt; rest&lt;br /&gt;&lt;span style="color:#676767"&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color:#696969; font-weight:bold"&gt;function&lt;/span&gt; commentIndex&lt;span style="color:#676767"&gt;(&lt;/span&gt;txt&lt;span style="color:#676767"&gt;,&lt;/span&gt; commtxt&lt;span style="color:#676767"&gt;,&lt;/span&gt;      n&lt;span style="color:#676767"&gt;) {&lt;/span&gt;&lt;br /&gt;  n &lt;span style="color:#676767"&gt;=&lt;/span&gt; &lt;span style="color:#2e8b57; font-weight:bold"&gt;index&lt;/span&gt;&lt;span style="color:#676767"&gt;(&lt;/span&gt;txt&lt;span style="color:#676767"&gt;,&lt;/span&gt; commtxt&lt;span style="color:#676767"&gt;)&lt;/span&gt;&lt;br /&gt;  &lt;span style="color:#2e8b57; font-weight:bold"&gt;if&lt;/span&gt; &lt;span style="color:#676767"&gt;(&lt;/span&gt;n &lt;span style="color:#676767"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#ff1493"&gt;0&lt;/span&gt; &lt;span style="color:#676767"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span style="color:#2e8b57; font-weight:bold"&gt;substr&lt;/span&gt;&lt;span style="color:#676767"&gt;(&lt;/span&gt;txt&lt;span style="color:#676767"&gt;,&lt;/span&gt; &lt;span style="color:#ff1493"&gt;1&lt;/span&gt;&lt;span style="color:#676767"&gt;,&lt;/span&gt; n&lt;span style="color:#676767"&gt;-&lt;/span&gt;&lt;span style="color:#ff1493"&gt;1&lt;/span&gt;&lt;span style="color:#676767"&gt;) !&lt;/span&gt;~ &lt;span style="color:#676767"&gt;/&lt;/span&gt;^&lt;span style="color:#676767"&gt;[&lt;/span&gt; \t&lt;span style="color:#676767"&gt;]*&lt;/span&gt;$&lt;span style="color:#676767"&gt;/) {&lt;/span&gt;&lt;br /&gt;    n &lt;span style="color:#676767"&gt;=&lt;/span&gt; &lt;span style="color:#ff1493"&gt;0&lt;/span&gt;&lt;br /&gt;  &lt;span style="color:#676767"&gt;}&lt;/span&gt;&lt;br /&gt;  &lt;span style="color:#2e8b57; font-weight:bold"&gt;return&lt;/span&gt; n&lt;br /&gt;&lt;span style="color:#676767"&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color:#696969; font-weight:bold"&gt;function&lt;/span&gt; noncommentPrefix&lt;span style="color:#676767"&gt;(&lt;/span&gt;txt&lt;span style="color:#676767"&gt;) {&lt;/span&gt;&lt;br /&gt;  &lt;span style="color:#2e8b57; font-weight:bold"&gt;return match&lt;/span&gt;&lt;span style="color:#676767"&gt;(&lt;/span&gt;txt&lt;span style="color:#676767"&gt;, /&lt;/span&gt;^&lt;span style="color:#676767"&gt;[&lt;/span&gt; \t&lt;span style="color:#676767"&gt;]*/)&lt;/span&gt; ? &lt;span style="color:#2e8b57; font-weight:bold"&gt;substr&lt;/span&gt;&lt;span style="color:#676767"&gt;(&lt;/span&gt;txt&lt;span style="color:#676767"&gt;,&lt;/span&gt; &lt;span style="color:#ff1493"&gt;1&lt;/span&gt;&lt;span style="color:#676767"&gt;,&lt;/span&gt; &lt;span style="color:#2e8b57; font-weight:bold"&gt;RLENGTH&lt;/span&gt;&lt;span style="color:#676767"&gt;) :&lt;/span&gt; &lt;span style="color:#ff1493"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;br /&gt;&lt;span style="color:#676767"&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color:#696969; font-weight:bold"&gt;function&lt;/span&gt; multiString&lt;span style="color:#676767"&gt;(&lt;/span&gt;str&lt;span style="color:#676767"&gt;,&lt;/span&gt; mult&lt;span style="color:#676767"&gt;,&lt;/span&gt;     n&lt;span style="color:#676767"&gt;,&lt;/span&gt; mstr&lt;span style="color:#676767"&gt;) {&lt;/span&gt;&lt;br /&gt;  mstr &lt;span style="color:#676767"&gt;=&lt;/span&gt; &lt;span style="color:#ff1493"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;br /&gt;  &lt;span style="color:#2e8b57; font-weight:bold"&gt;for&lt;/span&gt; &lt;span style="color:#676767"&gt;(&lt;/span&gt;n&lt;span style="color:#676767"&gt;=&lt;/span&gt;&lt;span style="color:#ff1493"&gt;0&lt;/span&gt;&lt;span style="color:#676767"&gt;;&lt;/span&gt; n&lt;span style="color:#676767"&gt;&amp;lt;&lt;/span&gt;mult&lt;span style="color:#676767"&gt;;&lt;/span&gt; n&lt;span style="color:#676767"&gt;++) {&lt;/span&gt;&lt;br /&gt;    mstr &lt;span style="color:#676767"&gt;=&lt;/span&gt; mstr str&lt;br /&gt;  &lt;span style="color:#676767"&gt;}&lt;/span&gt;&lt;br /&gt;  &lt;span style="color:#2e8b57; font-weight:bold"&gt;return&lt;/span&gt; mstr&lt;br /&gt;&lt;span style="color:#676767"&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color:#696969; font-weight:bold"&gt;function&lt;/span&gt; offsetString&lt;span style="color:#676767"&gt;(&lt;/span&gt;offset&lt;span style="color:#676767"&gt;) {&lt;/span&gt;&lt;br /&gt;  &lt;span style="color:#2e8b57; font-weight:bold"&gt;return&lt;/span&gt; multiString&lt;span style="color:#676767"&gt;(&lt;/span&gt;&lt;span style="color:#ff1493"&gt;&amp;quot; &amp;quot;&lt;/span&gt;&lt;span style="color:#676767"&gt;,&lt;/span&gt; offset&lt;span style="color:#676767"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;span style="color:#676767"&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;BEGIN&lt;/span&gt; &lt;span style="color:#676767"&gt;{&lt;/span&gt;&lt;br /&gt;  TabWidth&lt;span style="color:#676767"&gt;=&lt;/span&gt;&lt;span style="color:#ff1493"&gt;8&lt;/span&gt;&lt;br /&gt;  CommentString&lt;span style="color:#676767"&gt;=&lt;/span&gt;&lt;span style="color:#ff1493"&gt;&amp;quot;#&amp;quot;&lt;/span&gt;&lt;br /&gt;&lt;span style="color:#676767"&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;NR&lt;/span&gt; &lt;span style="color:#676767"&gt;==&lt;/span&gt; &lt;span style="color:#ff1493"&gt;1&lt;/span&gt; &lt;span style="color:#676767"&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span style="color:#2929cc"&gt;# Establish regex based on tab settings. This comes after&lt;/span&gt;&lt;br /&gt;  &lt;span style="color:#2929cc"&gt;# the BEGIN to allow the TabWidth to be overriden.&lt;/span&gt;&lt;br /&gt;  indentRegex &lt;span style="color:#676767"&gt;=&lt;/span&gt; &lt;span style="color:#ff1493"&gt;&amp;quot;( {0,&amp;quot;&lt;/span&gt;&lt;span style="color:#676767"&gt;(&lt;/span&gt;TabWidth&lt;span style="color:#676767"&gt;-&lt;/span&gt;&lt;span style="color:#ff1493"&gt;1&lt;/span&gt;&lt;span style="color:#676767"&gt;)&lt;/span&gt;&lt;span style="color:#ff1493"&gt;&amp;quot;}&lt;/span&gt;&lt;span style="color:#6a5acd"&gt;\t&lt;/span&gt;&lt;span style="color:#ff1493"&gt;| {&amp;quot;&lt;/span&gt;TabWidth&lt;span style="color:#ff1493"&gt;&amp;quot;})&amp;quot;&lt;/span&gt;&lt;br /&gt;  &lt;span style="color:#2929cc"&gt;# indentRegex = &amp;quot;( {0,3}\t|&amp;quot;offsetString(TabWidth)&amp;quot;)&amp;quot;&lt;/span&gt;&lt;br /&gt;&lt;span style="color:#676767"&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color:#676767"&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span style="color:#2929cc"&gt;# Common processing for all lines. Divide the line into a&lt;/span&gt;&lt;br /&gt;  &lt;span style="color:#2929cc"&gt;# prefix of whitespace, followed immediately by the&lt;/span&gt;&lt;br /&gt;  &lt;span style="color:#2929cc"&gt;# comment string, if present, or a non-tab, non-space&lt;/span&gt;&lt;br /&gt;  &lt;span style="color:#2929cc"&gt;# character if not. The prefix consists of indentation&lt;/span&gt;&lt;br /&gt;  &lt;span style="color:#2929cc"&gt;# steps followed by an offset, defined as a number of spaces&lt;/span&gt;&lt;br /&gt;  &lt;span style="color:#2929cc"&gt;# insufficient to constitute an indentation step.&lt;/span&gt;&lt;br /&gt;  Line&lt;span style="color:#676767"&gt;[&lt;/span&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;NR&lt;/span&gt;&lt;span style="color:#676767"&gt;] =&lt;/span&gt; $&lt;span style="color:#ff1493"&gt;0&lt;/span&gt;&lt;br /&gt;  commInd &lt;span style="color:#676767"&gt;=&lt;/span&gt; commentIndex&lt;span style="color:#676767"&gt;(&lt;/span&gt;$&lt;span style="color:#ff1493"&gt;0&lt;/span&gt;&lt;span style="color:#676767"&gt;,&lt;/span&gt; CommentString&lt;span style="color:#676767"&gt;)&lt;/span&gt;&lt;br /&gt;  &lt;span style="color:#2e8b57; font-weight:bold"&gt;if&lt;/span&gt; &lt;span style="color:#676767"&gt;(&lt;/span&gt;commInd &lt;span style="color:#676767"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#ff1493"&gt;0&lt;/span&gt;&lt;span style="color:#676767"&gt;) {&lt;/span&gt;&lt;br /&gt;    prefix &lt;span style="color:#676767"&gt;=&lt;/span&gt; &lt;span style="color:#2e8b57; font-weight:bold"&gt;substr&lt;/span&gt;&lt;span style="color:#676767"&gt;(&lt;/span&gt;$&lt;span style="color:#ff1493"&gt;0&lt;/span&gt;&lt;span style="color:#676767"&gt;,&lt;/span&gt; &lt;span style="color:#ff1493"&gt;1&lt;/span&gt;&lt;span style="color:#676767"&gt;,&lt;/span&gt; commInd&lt;span style="color:#676767"&gt;-&lt;/span&gt;&lt;span style="color:#ff1493"&gt;1&lt;/span&gt;&lt;span style="color:#676767"&gt;)&lt;/span&gt;&lt;br /&gt;    CommentPosition&lt;span style="color:#676767"&gt;[&lt;/span&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;NR&lt;/span&gt;&lt;span style="color:#676767"&gt;] =&lt;/span&gt; commInd&lt;br /&gt;  &lt;span style="color:#676767"&gt;}&lt;/span&gt; &lt;span style="color:#2e8b57; font-weight:bold"&gt;else&lt;/span&gt; &lt;span style="color:#676767"&gt;{&lt;/span&gt;&lt;br /&gt;    prefix &lt;span style="color:#676767"&gt;=&lt;/span&gt; noncommentPrefix&lt;span style="color:#676767"&gt;(&lt;/span&gt;$&lt;span style="color:#ff1493"&gt;0&lt;/span&gt;&lt;span style="color:#676767"&gt;)&lt;/span&gt;&lt;br /&gt;  &lt;span style="color:#676767"&gt;}&lt;/span&gt;&lt;br /&gt;  offset &lt;span style="color:#676767"&gt;=&lt;/span&gt; &lt;span style="color:#2e8b57; font-weight:bold"&gt;length&lt;/span&gt;&lt;span style="color:#676767"&gt;(&lt;/span&gt;trimIndent&lt;span style="color:#676767"&gt;(&lt;/span&gt;prefix&lt;span style="color:#676767"&gt;,&lt;/span&gt; indentRegex&lt;span style="color:#676767"&gt;))&lt;/span&gt;&lt;br /&gt;&lt;span style="color:#676767"&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;NR&lt;/span&gt; &lt;span style="color:#676767"&gt;==&lt;/span&gt; &lt;span style="color:#ff1493"&gt;1&lt;/span&gt; &lt;span style="color:#676767"&gt;{&lt;/span&gt;&lt;br /&gt;  BaseOffset &lt;span style="color:#676767"&gt;=&lt;/span&gt; offset&lt;br /&gt;  BaseIndent &lt;span style="color:#676767"&gt;=&lt;/span&gt; IndentLevel&lt;br /&gt;  MinOffset &lt;span style="color:#676767"&gt;=&lt;/span&gt; BaseOffset&lt;br /&gt;  MinIndent &lt;span style="color:#676767"&gt;=&lt;/span&gt; BaseIndent&lt;br /&gt;&lt;span style="color:#676767"&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;NR&lt;/span&gt; &lt;span style="color:#676767"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#ff1493"&gt;1&lt;/span&gt; &lt;span style="color:#676767"&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span style="color:#2e8b57; font-weight:bold"&gt;if&lt;/span&gt; &lt;span style="color:#676767"&gt;(&lt;/span&gt;IndentLevel &lt;span style="color:#676767"&gt;&amp;lt;&lt;/span&gt; MinIndent &lt;span style="color:#676767"&gt;) {&lt;/span&gt;&lt;br /&gt;    MinIndent &lt;span style="color:#676767"&gt;=&lt;/span&gt; IndentLevel&lt;br /&gt;    MinOffset &lt;span style="color:#676767"&gt;=&lt;/span&gt; &lt;span style="color:#ff1493"&gt;0&lt;/span&gt;&lt;br /&gt;  &lt;span style="color:#676767"&gt;}&lt;/span&gt; &lt;span style="color:#2e8b57; font-weight:bold"&gt;else if&lt;/span&gt; &lt;span style="color:#676767"&gt;(&lt;/span&gt;offset &lt;span style="color:#676767"&gt;&amp;lt;&lt;/span&gt; MinOffset&lt;span style="color:#676767"&gt;) {&lt;/span&gt;&lt;br /&gt;    MinOffset &lt;span style="color:#676767"&gt;=&lt;/span&gt; offset&lt;br /&gt;  &lt;span style="color:#676767"&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span style="color:#676767"&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;END&lt;/span&gt; &lt;span style="color:#676767"&gt;{&lt;/span&gt;&lt;br /&gt;  commLen &lt;span style="color:#676767"&gt;=&lt;/span&gt; &lt;span style="color:#2e8b57; font-weight:bold"&gt;length&lt;/span&gt;&lt;span style="color:#676767"&gt;(&lt;/span&gt;CommentString&lt;span style="color:#676767"&gt;)&lt;/span&gt;&lt;br /&gt;  &lt;span style="color:#2e8b57; font-weight:bold"&gt;if&lt;/span&gt; &lt;span style="color:#676767"&gt;(&lt;/span&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;length&lt;/span&gt;&lt;span style="color:#676767"&gt;(&lt;/span&gt;CommentPosition&lt;span style="color:#676767"&gt;) ==&lt;/span&gt; &lt;span style="color:#2e8b57; font-weight:bold"&gt;length&lt;/span&gt;&lt;span style="color:#676767"&gt;(&lt;/span&gt;Line&lt;span style="color:#676767"&gt;) &amp;amp;&amp;amp;&lt;/span&gt; MinIndent &lt;span style="color:#676767"&gt;==&lt;/span&gt; BaseIndent &lt;span style="color:#676767"&gt;&amp;amp;&amp;amp;&lt;/span&gt; MinOffset &lt;span style="color:#676767"&gt;==&lt;/span&gt; BaseOffset&lt;span style="color:#676767"&gt;) {&lt;/span&gt;&lt;br /&gt;    &lt;span style="color:#2e8b57; font-weight:bold"&gt;for&lt;/span&gt; &lt;span style="color:#676767"&gt;(&lt;/span&gt;n&lt;span style="color:#676767"&gt;=&lt;/span&gt;&lt;span style="color:#ff1493"&gt;1&lt;/span&gt;&lt;span style="color:#676767"&gt;;&lt;/span&gt; n&lt;span style="color:#676767"&gt;&amp;lt;=&lt;/span&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;NR&lt;/span&gt;&lt;span style="color:#676767"&gt;;&lt;/span&gt; n&lt;span style="color:#676767"&gt;++) {&lt;/span&gt;&lt;br /&gt;      commPos &lt;span style="color:#676767"&gt;=&lt;/span&gt; CommentPosition&lt;span style="color:#676767"&gt;[&lt;/span&gt;n&lt;span style="color:#676767"&gt;]&lt;/span&gt;&lt;br /&gt;      &lt;span style="color:#2e8b57; font-weight:bold"&gt;print substr&lt;/span&gt;&lt;span style="color:#676767"&gt;(&lt;/span&gt;Line&lt;span style="color:#676767"&gt;[&lt;/span&gt;n&lt;span style="color:#676767"&gt;],&lt;/span&gt; &lt;span style="color:#ff1493"&gt;1&lt;/span&gt;&lt;span style="color:#676767"&gt;,&lt;/span&gt; commPos&lt;span style="color:#676767"&gt;-&lt;/span&gt;&lt;span style="color:#ff1493"&gt;1&lt;/span&gt;&lt;span style="color:#676767"&gt;)&lt;/span&gt; &lt;span style="color:#2e8b57; font-weight:bold"&gt;substr&lt;/span&gt;&lt;span style="color:#676767"&gt;(&lt;/span&gt;Line&lt;span style="color:#676767"&gt;[&lt;/span&gt;n&lt;span style="color:#676767"&gt;],&lt;/span&gt; commLen&lt;span style="color:#676767"&gt;+&lt;/span&gt;commPos&lt;span style="color:#676767"&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span style="color:#676767"&gt;}&lt;/span&gt;&lt;br /&gt;  &lt;span style="color:#676767"&gt;}&lt;/span&gt; &lt;span style="color:#2e8b57; font-weight:bold"&gt;else&lt;/span&gt; &lt;span style="color:#676767"&gt;{&lt;/span&gt;&lt;br /&gt;    indentPart &lt;span style="color:#676767"&gt;=&lt;/span&gt; MinIndent ? indentRegex&lt;span style="color:#ff1493"&gt;&amp;quot;{&amp;quot;&lt;/span&gt;MinIndent&lt;span style="color:#ff1493"&gt;&amp;quot;}&amp;quot;&lt;/span&gt; &lt;span style="color:#676767"&gt;:&lt;/span&gt; &lt;span style="color:#ff1493"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;br /&gt;    &lt;span style="color:#2929cc"&gt;# indentPart = multiString(indentRegex, MinIndent)&lt;/span&gt;&lt;br /&gt;    offsetPart &lt;span style="color:#676767"&gt;=&lt;/span&gt; offsetString&lt;span style="color:#676767"&gt;(&lt;/span&gt;MinOffset&lt;span style="color:#676767"&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span style="color:#2929cc"&gt;# offsetPart = offsetString(MinOffset)&lt;/span&gt;&lt;br /&gt;    commRegex &lt;span style="color:#676767"&gt;=&lt;/span&gt; &lt;span style="color:#ff1493"&gt;&amp;quot;^&amp;quot;&lt;/span&gt; indentPart offsetPart&lt;br /&gt;    &lt;span style="color:#2e8b57; font-weight:bold"&gt;for&lt;/span&gt; &lt;span style="color:#676767"&gt;(&lt;/span&gt;n&lt;span style="color:#676767"&gt;=&lt;/span&gt;&lt;span style="color:#ff1493"&gt;1&lt;/span&gt;&lt;span style="color:#676767"&gt;;&lt;/span&gt; n&lt;span style="color:#676767"&gt;&amp;lt;=&lt;/span&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;NR&lt;/span&gt;&lt;span style="color:#676767"&gt;;&lt;/span&gt; n&lt;span style="color:#676767"&gt;++) {&lt;/span&gt;&lt;br /&gt;      &lt;span style="color:#2e8b57; font-weight:bold"&gt;match&lt;/span&gt;&lt;span style="color:#676767"&gt;(&lt;/span&gt;Line&lt;span style="color:#676767"&gt;[&lt;/span&gt;n&lt;span style="color:#676767"&gt;],&lt;/span&gt; commRegex&lt;span style="color:#676767"&gt;)&lt;/span&gt;&lt;br /&gt;      &lt;span style="color:#2e8b57; font-weight:bold"&gt;print substr&lt;/span&gt;&lt;span style="color:#676767"&gt;(&lt;/span&gt;Line&lt;span style="color:#676767"&gt;[&lt;/span&gt;n&lt;span style="color:#676767"&gt;],&lt;/span&gt; &lt;span style="color:#ff1493"&gt;1&lt;/span&gt;&lt;span style="color:#676767"&gt;,&lt;/span&gt; &lt;span style="color:#2e8b57; font-weight:bold"&gt;RLENGTH&lt;/span&gt;&lt;span style="color:#676767"&gt;)&lt;/span&gt; CommentString &lt;span style="color:#2e8b57; font-weight:bold"&gt;substr&lt;/span&gt;&lt;span style="color:#676767"&gt;(&lt;/span&gt;Line&lt;span style="color:#676767"&gt;[&lt;/span&gt;n&lt;span style="color:#676767"&gt;],&lt;/span&gt; &lt;span style="color:#ff1493"&gt;1&lt;/span&gt;&lt;span style="color:#676767"&gt;+&lt;/span&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;RLENGTH&lt;/span&gt;&lt;span style="color:#676767"&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span style="color:#676767"&gt;}&lt;/span&gt;&lt;br /&gt;  &lt;span style="color:#676767"&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span style="color:#676767"&gt;}&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3164056879958238384-551589279712947384?l=appliedabstraction.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://appliedabstraction.blogspot.com/feeds/551589279712947384/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3164056879958238384&amp;postID=551589279712947384' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/551589279712947384'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/551589279712947384'/><link rel='alternate' type='text/html' href='http://appliedabstraction.blogspot.com/2010/02/better-comment-script.html' title='A Better Comment Script'/><author><name>Michael Barber</name><uri>http://www.blogger.com/profile/15713879666980145844</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3164056879958238384.post-9105072215668440021</id><published>2010-02-27T08:17:00.001-08:00</published><updated>2010-04-08T12:38:30.089-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='TaskPaper'/><category scheme='http://www.blogger.com/atom/ns#' term='time management'/><category scheme='http://www.blogger.com/atom/ns#' term='SubEthaEdit'/><category scheme='http://www.blogger.com/atom/ns#' term='shell'/><title type='text'>TaskPaper Mode for SubEthaEdit</title><content type='html'>&lt;a href="http://www.hogbaysoftware.com/products/taskpaper"&gt;TaskPaper&lt;/a&gt; is an application for managing simple to-do lists. It is somewhere between a text editor and an outline processor, focused on lists of to-do items that can be checked off. The lists can be organized into projects and marked up with tags, enabling search and selection by tag. &lt;br /&gt;&lt;br /&gt;TaskPaper saves its documents as plain text. They can be opened, modified, and created by any application that can work with text files. In fact, it is fair to say that TaskPaper is both an application and a lightweight text-markup system specifically for to-do lists. It is pretty easy to support the TaskPaper file format, and it &lt;a href="http://www.hogbaysoftware.com/wiki/TaskPaperRelatedProjects"&gt;has been done&lt;/a&gt; for several text editors. &lt;br /&gt;&lt;br /&gt;Some months back, I created a ToDo mode for &lt;a href="http://www.subethaedit.net/"&gt;SubEthaEdit&lt;/a&gt; that supports the TaskPaper format. Today, I finally got around to making it available for &lt;a href="http://www.box.net/shared/kg3jzb9leh"&gt;download&lt;/a&gt;. The mode supports syntax highlighting and has scripts to automate creating new tasks and projects, marking tasks as done, and archiving completed tasks. Tags are detected and highlighted, but there is unfortunately no way to do the outline-processor-style hoisting of particular tags. To aid in managing multiple tasks, project names appear in the function popup menu.&lt;br /&gt;&lt;br /&gt;It is also possible to modify how the mode handles marking tasks as completed and archiving them. This requires an additional &lt;a href="http://www.box.net/shared/ebfodi5eso"&gt;script&lt;/a&gt; to open a plist of environment settings; install this script in the scripts folder for SubEthaEdit (if you're not sure where that is, use &lt;em&gt;Open Scripts Folder&lt;/em&gt; under the scripts menu in SEE). Two relevant keys can be set, &lt;code&gt;SEE_TODO_MARK_DONE&lt;/code&gt; and &lt;code&gt;SEE_TODO_ARCHIVE_DONE&lt;/code&gt;. The values should be set to shell commands that implement the desired behavior for marking tasks as completed and for moving completed tasks to the Archive pseudo-project. &lt;br /&gt;&lt;br /&gt;One possibility is to pass different command-line options to the scripts that implement the default behavior for the mode. For example, you could set the value of &lt;code&gt;SEE_TODO_MARK_DONE&lt;/code&gt; to &lt;code&gt;'"$SEE_MODE_RESOURCES"/bin/markdone.sh -c -t'&lt;/code&gt; and the value of &lt;code&gt;SEE_TODO_ARCHIVE_DONE&lt;/code&gt; to &lt;code&gt;'"$SEE_MODE_RESOURCES/bin/archivecompleted.awk" -v Mode=c'&lt;/code&gt; (including the quotes in the values). With these flags, tasks are no longer marked complete with a &lt;code&gt;@done&lt;/code&gt; tag, but instead the leading hyphen is turned into a plus sign, giving a sort of check-off effect instead of a tagging effect. Be aware that this breaks compatibility with the TaskPaper application.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Update:&lt;/strong&gt; The ToDo mode is available on the &lt;a href="http://www.codingmonkeys.de/subethaedit/modes.html"&gt;Coding Monkeys&lt;/a&gt; website.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3164056879958238384-9105072215668440021?l=appliedabstraction.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://appliedabstraction.blogspot.com/feeds/9105072215668440021/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3164056879958238384&amp;postID=9105072215668440021' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/9105072215668440021'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/9105072215668440021'/><link rel='alternate' type='text/html' href='http://appliedabstraction.blogspot.com/2010/02/taskpaper-mode-for-subethaedit.html' title='TaskPaper Mode for SubEthaEdit'/><author><name>Michael Barber</name><uri>http://www.blogger.com/profile/15713879666980145844</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3164056879958238384.post-4079228875856192432</id><published>2010-02-20T11:14:00.001-08:00</published><updated>2010-04-17T11:54:28.272-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ctags'/><category scheme='http://www.blogger.com/atom/ns#' term='AppleScript'/><category scheme='http://www.blogger.com/atom/ns#' term='SubEthaEdit'/><category scheme='http://www.blogger.com/atom/ns#' term='shell'/><title type='text'>Exploring Ctags: Summary</title><content type='html'>To facilitate learning about Ctags, I've written two AppleScripts and several supporting shell scripts. These scripts were not written by an expert on Ctags, so there may be some sub-optimal, or outright wrong, choices in how they were implemented. Please let me know of any bugs found or suggestions for possible improvements.&lt;br /&gt;&lt;br /&gt;The AppleScripts use Ctags to add a couple of features to SubEthaEdit (SEE). First, there is the text completion AppleScript, which looks up a string in the tag file and identifies possible matches. SEE already does text completion, but only in open files; by using Ctags as a basis for completions, matching symbols can be found across all the files in a large project. The second AppleScript finds definitions of selected symbols, again facilitating working with a large number of files. &lt;br /&gt;&lt;br /&gt;The interactions with the tag file are handled using shell scripts. These are written to handle tag files created by invoking Exuberant Ctags with a variety of different options, notably including either absolute or relative paths and either numeric or &lt;code&gt;ex&lt;/code&gt; pattern references for the location in the files. The shell scripts need to be placed somewhere on the paths defined in the AppleScripts; if in doubt, &lt;code&gt;~/Library/Application Support/SubEthaEdit/bin/&lt;/code&gt; will work.&lt;br /&gt;&lt;br /&gt;A zip archive with the scripts is available for &lt;a href="http://www.box.net/shared/zksls0d28x"&gt;download&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;The scripts are described in a series of blog posts:&lt;ol&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://appliedabstraction.blogspot.com/2010/02/exploring-ctags-1-motivations.html"&gt;Exploring Ctags: Motivations&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://appliedabstraction.blogspot.com/2010/02/find-that-tags-file.html"&gt;Find That Tags File!&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://appliedabstraction.blogspot.com/2010/02/tag-matching.html"&gt;Tag Matching&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://appliedabstraction.blogspot.com/2010/02/ctags-in-subethaedit.html"&gt;Ctags in SubEthaEdit&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://appliedabstraction.blogspot.com/2010/02/ctags-from-subethaedit-to-shell.html"&gt;Ctags from SubEthaEdit to the Shell&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://appliedabstraction.blogspot.com/2010/02/text-completions-with-ctags-in.html"&gt;Text Completions with Ctags in SubEthaEdit&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://appliedabstraction.blogspot.com/2010/02/finding-definitions-with-ctags-in.html"&gt;Finding Definitions with Ctags in SubEthaEdit&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;/ol&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Update:&lt;/strong&gt; I've added another AppleScript and accompanying shell script for creating or updating a tag file for the front document in SEE. These are now in the zip archive, available at the same download link given above.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3164056879958238384-4079228875856192432?l=appliedabstraction.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://appliedabstraction.blogspot.com/feeds/4079228875856192432/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3164056879958238384&amp;postID=4079228875856192432' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/4079228875856192432'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/4079228875856192432'/><link rel='alternate' type='text/html' href='http://appliedabstraction.blogspot.com/2010/02/exploring-ctags-summary.html' title='Exploring Ctags: Summary'/><author><name>Michael Barber</name><uri>http://www.blogger.com/profile/15713879666980145844</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3164056879958238384.post-3569232555814016237</id><published>2010-02-20T10:38:00.001-08:00</published><updated>2010-02-20T10:38:06.747-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ctags'/><category scheme='http://www.blogger.com/atom/ns#' term='AppleScript'/><category scheme='http://www.blogger.com/atom/ns#' term='SubEthaEdit'/><category scheme='http://www.blogger.com/atom/ns#' term='shell'/><title type='text'>Finding Definitions with Ctags in SubEthaEdit</title><content type='html'>As with using Ctags for &lt;a href="http://appliedabstraction.blogspot.com/2010/02/text-completions-with-ctags-in.html"&gt;text completion&lt;/a&gt;, finding definitions for symbols can be expressed largely in terms of the shell scripts and AppleScript handlers already presented. Another handler, &lt;code&gt;openTaggedSources&lt;/code&gt;, is needed, which will open files to the location of the selected tag or tags.&lt;br /&gt;&lt;br /&gt;The resulting AppleScript is again quite concise:&lt;pre style="color:#000000; background-color:#fff5ee; font-size:10pt; font-family:'Courier New';"&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;on&lt;/span&gt; &lt;span style="color:#000069"&gt;seescriptsettings&lt;/span&gt;()&lt;br /&gt;  {displayName:&lt;span style="color:#ff1493"&gt;&amp;quot;Find Definition using Ctags&amp;quot;&lt;/span&gt;, shortDisplayName:&lt;span style="color:#ff1493"&gt;&amp;quot;Ctags Definition&amp;quot;&lt;/span&gt;, keyboardShortcut:&lt;span style="color:#ff1493"&gt;&amp;quot;&amp;#64;^f&amp;quot;&lt;/span&gt;, inContextMenu:&lt;span style="color:#ff1493"&gt;&amp;quot;yes&amp;quot;&lt;/span&gt;}&lt;br /&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;end&lt;/span&gt; seescriptsettings&lt;br /&gt;&lt;br /&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;try&lt;/span&gt;&lt;br /&gt;  &lt;span style="color:#000069"&gt;requireValidDocumentForCtags&lt;/span&gt;()&lt;br /&gt;  &lt;span style="color:#2e8b57; font-weight:bold"&gt;set&lt;/span&gt; tagfilepath &lt;span style="color:#2e8b57; font-weight:bold"&gt;to&lt;/span&gt; &lt;span style="color:#000069"&gt;findTagFile&lt;/span&gt;()&lt;br /&gt;  &lt;span style="color:#2e8b57; font-weight:bold"&gt;set&lt;/span&gt; searchTerm &lt;span style="color:#2e8b57; font-weight:bold"&gt;to&lt;/span&gt; determineSearchTerm &lt;span style="color:#2e8b57; font-weight:bold"&gt;with&lt;/span&gt; userIntervention&lt;br /&gt;  &lt;span style="color:#2e8b57; font-weight:bold"&gt;set&lt;/span&gt; taglist &lt;span style="color:#2e8b57; font-weight:bold"&gt;to&lt;/span&gt; (pipeMatches &lt;span style="color:#2e8b57; font-weight:bold"&gt;of&lt;/span&gt; searchTerm out &lt;span style="color:#2e8b57; font-weight:bold"&gt;of&lt;/span&gt; tagfilepath thru &lt;span style="color:#ff1493"&gt;&amp;quot;&amp;quot;&lt;/span&gt;)&lt;br /&gt;  &lt;span style="color:#2e8b57; font-weight:bold"&gt;set&lt;/span&gt; tagsToOpen &lt;span style="color:#2e8b57; font-weight:bold"&gt;to&lt;/span&gt; (pickTags &lt;span style="color:#2e8b57; font-weight:bold"&gt;from&lt;/span&gt; taglist &lt;span style="color:#2e8b57; font-weight:bold"&gt;with&lt;/span&gt; multipleSelectionsAllowed)&lt;br /&gt;  openTaggedSources &lt;span style="color:#696969; font-weight:bold"&gt;for&lt;/span&gt; tagsToOpen &lt;span style="color:#2e8b57; font-weight:bold"&gt;from&lt;/span&gt; tagfilepath&lt;br /&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;on error&lt;/span&gt; errMsg &lt;span style="color:#696969; font-weight:bold"&gt;number&lt;/span&gt; errNum&lt;br /&gt;  &lt;span style="color:#2e8b57; font-weight:bold"&gt;if&lt;/span&gt; errNum &lt;span style="color:#2e8b57; font-weight:bold"&gt;is equal to&lt;/span&gt; &lt;span style="color:#ff1493"&gt;901&lt;/span&gt; &lt;span style="color:#2e8b57; font-weight:bold"&gt;then&lt;/span&gt;&lt;br /&gt;    &lt;span style="color:#2e8b57; font-weight:bold"&gt;return&lt;/span&gt;&lt;br /&gt;  &lt;span style="color:#2e8b57; font-weight:bold"&gt;else if&lt;/span&gt; errNum &lt;span style="color:#2e8b57; font-weight:bold"&gt;is equal to&lt;/span&gt; &lt;span style="color:#ff1493"&gt;902&lt;/span&gt; &lt;span style="color:#2e8b57; font-weight:bold"&gt;then&lt;/span&gt;&lt;br /&gt;    beep&lt;br /&gt;    &lt;span style="color:#2e8b57; font-weight:bold"&gt;return&lt;/span&gt;&lt;br /&gt;  &lt;span style="color:#2e8b57; font-weight:bold"&gt;else&lt;/span&gt;&lt;br /&gt;    &lt;span style="color:#2e8b57; font-weight:bold"&gt;error&lt;/span&gt; errMsg &lt;span style="color:#696969; font-weight:bold"&gt;number&lt;/span&gt; errNum&lt;br /&gt;  &lt;span style="color:#2e8b57; font-weight:bold"&gt;end if&lt;/span&gt;&lt;br /&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;end try&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;The structure directly parallels that used for the text completion script.&lt;br /&gt;&lt;br /&gt;Let's take a look inside the &lt;code&gt;openTaggedSources&lt;/code&gt; handler. My approach is to dump all the selected tags back to the shell, where the shell script &lt;code&gt;open-tag-files&lt;/code&gt; will finish the job. Here's the handler:&lt;br /&gt;&lt;pre style="color:#000000; background-color:#fff5ee; font-size:10pt; font-family:'Courier New';"&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;to&lt;/span&gt; openTaggedSources &lt;span style="color:#696969; font-weight:bold"&gt;for&lt;/span&gt; tags &lt;span style="color:#2e8b57; font-weight:bold"&gt;from&lt;/span&gt; tagfile&lt;br /&gt;  &lt;span style="color:#2929cc"&gt;--pass tags to external script that opens them in SEE&lt;/span&gt;&lt;br /&gt;  &lt;span style="color:#2e8b57; font-weight:bold"&gt;set&lt;/span&gt; exportTagsFile &lt;span style="color:#2e8b57; font-weight:bold"&gt;to&lt;/span&gt; &lt;span style="color:#ff1493"&gt;&amp;quot;export TAGDIR=&lt;/span&gt;&lt;span style="color:#6a5acd"&gt;\&amp;quot;&lt;/span&gt;&lt;span style="color:#ff1493"&gt;$(dirname &amp;quot;&lt;/span&gt; &amp;amp; (quoted form &lt;span style="color:#2e8b57; font-weight:bold"&gt;of&lt;/span&gt; tagfile) &amp;amp; &lt;span style="color:#ff1493"&gt;&amp;quot;)&lt;/span&gt;&lt;span style="color:#6a5acd"&gt;\&amp;quot;&lt;/span&gt;&lt;span style="color:#ff1493"&gt;;&amp;quot;&lt;/span&gt;&lt;br /&gt;  &lt;span style="color:#2e8b57; font-weight:bold"&gt;set&lt;/span&gt; openTagFilesPipeline &lt;span style="color:#2e8b57; font-weight:bold"&gt;to&lt;/span&gt; join &lt;span style="color:#2e8b57; font-weight:bold"&gt;of&lt;/span&gt; {&lt;span style="color:#ff1493"&gt;&amp;quot;printf &amp;quot;&lt;/span&gt; &amp;amp; quoted form &lt;span style="color:#2e8b57; font-weight:bold"&gt;of&lt;/span&gt; tags, &lt;span style="color:#ff1493"&gt;&amp;quot;open-tag-files RelTo=&lt;/span&gt;&lt;span style="color:#6a5acd"&gt;\&amp;quot;&lt;/span&gt;&lt;span style="color:#ff1493"&gt;$TAGDIR&lt;/span&gt;&lt;span style="color:#6a5acd"&gt;\&amp;quot;&lt;/span&gt;&lt;span style="color:#ff1493"&gt;&amp;quot;&lt;/span&gt;} by &lt;span style="color:#ff1493"&gt;&amp;quot;|&amp;quot;&lt;/span&gt;&lt;br /&gt;  &lt;span style="color:#2e8b57; font-weight:bold"&gt;set&lt;/span&gt; openTagFilesScript &lt;span style="color:#2e8b57; font-weight:bold"&gt;to&lt;/span&gt; join &lt;span style="color:#2e8b57; font-weight:bold"&gt;of&lt;/span&gt; {UnixPath, exportTagsFile, openTagFilesPipeline, &lt;span style="color:#ff1493"&gt;&amp;quot;&amp;amp;&amp;gt; /dev/null &amp;amp;&amp;quot;&lt;/span&gt;} by space&lt;br /&gt;  do shell &lt;span style="color:#2e8b57; font-weight:bold"&gt;script&lt;/span&gt; openTagFilesScript&lt;br /&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;end&lt;/span&gt; openTaggedSources&lt;br /&gt;&lt;/pre&gt;I pass the location of the tag file to the script, so that either absolute or relative paths can be used in the tag files. Otherwise, it's just passing the selected tags out as &lt;code&gt;stdin&lt;/code&gt; to &lt;code&gt; open-tag-files&lt;/code&gt; in a straightforward way. &lt;br /&gt;&lt;br /&gt;So let's look at &lt;code&gt; open-tag-files&lt;/code&gt;:&lt;pre style="color:#000000; background-color:#fff5ee; font-size:10pt; font-family:'Courier New';"&gt;#! /usr/bin/awk -f&lt;br /&gt;&lt;br /&gt;BEGIN {&lt;br /&gt;    FS=&lt;span style="color:#ff1493"&gt;&amp;quot;&lt;/span&gt;&lt;span style="color:#6a5acd"&gt;\t&lt;/span&gt;&lt;span style="color:#ff1493"&gt;&amp;quot;&lt;/span&gt;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;{&lt;br /&gt;    # Treat relative filenames &lt;span style="color:#2e8b57; font-weight:bold"&gt;as&lt;/span&gt; relative &lt;span style="color:#2e8b57; font-weight:bold"&gt;to&lt;/span&gt; RelTo&lt;br /&gt;    &lt;span style="color:#2e8b57; font-weight:bold"&gt;if&lt;/span&gt; ($&lt;span style="color:#ff1493"&gt;2&lt;/span&gt; ~ /^\//) {&lt;br /&gt;        filePath = $&lt;span style="color:#ff1493"&gt;2&lt;/span&gt;&lt;br /&gt;    } &lt;span style="color:#2e8b57; font-weight:bold"&gt;else&lt;/span&gt; {&lt;br /&gt;        filePath = RelTo &lt;span style="color:#ff1493"&gt;&amp;quot;/&amp;quot;&lt;/span&gt; $&lt;span style="color:#ff1493"&gt;2&lt;/span&gt;&lt;br /&gt;    }&lt;br /&gt;    # Handle both numeric &lt;span style="color:#2e8b57; font-weight:bold"&gt;and&lt;/span&gt; regex patterns&lt;br /&gt;    &lt;span style="color:#2e8b57; font-weight:bold"&gt;if&lt;/span&gt; ($&lt;span style="color:#ff1493"&gt;3&lt;/span&gt; ~ /^[[:digit:]]+(;\&amp;quot;)?$/) {&lt;br /&gt;        &lt;span style="color:#000069"&gt;match&lt;/span&gt;($&lt;span style="color:#ff1493"&gt;3&lt;/span&gt;, /^[[:digit:]]+/)&lt;br /&gt;        gotoLine = &lt;span style="color:#ff1493"&gt;&amp;quot;-g &amp;quot;&lt;/span&gt; &lt;span style="color:#000069"&gt;substr&lt;/span&gt;($&lt;span style="color:#ff1493"&gt;3&lt;/span&gt;, RSTART, RLENGTH)&lt;br /&gt;    } &lt;span style="color:#2e8b57; font-weight:bold"&gt;else&lt;/span&gt; {&lt;br /&gt;        patternPlusExtras = &lt;span style="color:#000069"&gt;substr&lt;/span&gt;($&lt;span style="color:#ff1493"&gt;0&lt;/span&gt;, &lt;span style="color:#000069"&gt;index&lt;/span&gt;($&lt;span style="color:#ff1493"&gt;0&lt;/span&gt;, $&lt;span style="color:#ff1493"&gt;3&lt;/span&gt;))&lt;br /&gt;        numTokens = &lt;span style="color:#000069"&gt;split&lt;/span&gt;(patternPlusExtras, token, &lt;span style="color:#ff1493"&gt;&amp;quot;/&amp;quot;&lt;/span&gt;)&lt;br /&gt;        &lt;span style="color:#2e8b57; font-weight:bold"&gt;if&lt;/span&gt; (&lt;span style="color:#696969; font-weight:bold"&gt;length&lt;/span&gt;(token[&lt;span style="color:#ff1493"&gt;1&lt;/span&gt;])) {&lt;br /&gt;            # Pattern looks invalid, so can't specify &lt;span style="color:#2e8b57; font-weight:bold"&gt;the&lt;/span&gt; line&lt;br /&gt;            gotoLine = &lt;span style="color:#ff1493"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;br /&gt;        } &lt;span style="color:#2e8b57; font-weight:bold"&gt;else&lt;/span&gt; {&lt;br /&gt;            exQuery = &lt;span style="color:#ff1493"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;br /&gt;            &lt;span style="color:#696969; font-weight:bold"&gt;for&lt;/span&gt; (n=&lt;span style="color:#ff1493"&gt;2&lt;/span&gt;; n&amp;lt;=numTokens; n++) {&lt;br /&gt;                exQuery = exQuery &lt;span style="color:#ff1493"&gt;&amp;quot;/&amp;quot;&lt;/span&gt; token[n]&lt;br /&gt;                &lt;span style="color:#2e8b57; font-weight:bold"&gt;if&lt;/span&gt; (token[n] !~ /[^\\](\\\\)*\\$/) {&lt;br /&gt;                    break&lt;br /&gt;                }&lt;br /&gt;            }&lt;br /&gt;            exQuery = exQuery &lt;span style="color:#ff1493"&gt;&amp;quot;/&amp;quot;&lt;/span&gt;&lt;br /&gt;            command = &lt;span style="color:#ff1493"&gt;&amp;quot;cat '&amp;quot;&lt;/span&gt;filePath&lt;span style="color:#ff1493"&gt;&amp;quot;' | sed -e '&amp;quot;&lt;/span&gt;exQuery&lt;span style="color:#ff1493"&gt;&amp;quot; q' | wc -l&amp;quot;&lt;/span&gt;&lt;br /&gt;            command | getline lineCount&lt;br /&gt;            &lt;span style="color:#696969; font-weight:bold"&gt;close&lt;/span&gt;(command)&lt;br /&gt;            gotoLine = &lt;span style="color:#ff1493"&gt;&amp;quot;-g &amp;quot;&lt;/span&gt;lineCount&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;    #&lt;span style="color:#000069"&gt;printf&lt;/span&gt;(&lt;span style="color:#ff1493"&gt;&amp;quot;see %s&lt;/span&gt; &lt;span style="color:#6a5acd"&gt;\&amp;quot;&lt;/span&gt;&lt;span style="color:#ff1493"&gt;%s&lt;/span&gt;&lt;span style="color:#6a5acd"&gt;\&amp;quot;&lt;/span&gt; &lt;span style="color:#ff1493"&gt;&amp;amp;&lt;/span&gt;&lt;span style="color:#6a5acd"&gt;\n&lt;/span&gt;&lt;span style="color:#ff1493"&gt;&amp;quot;&lt;/span&gt;, gotoLine, filePath)&lt;br /&gt;    &lt;span style="color:#000069"&gt;system&lt;/span&gt;(&lt;span style="color:#ff1493"&gt;&amp;quot;altsee &amp;quot;&lt;/span&gt;gotoLine&lt;span style="color:#ff1493"&gt;&amp;quot;&lt;/span&gt; &lt;span style="color:#6a5acd"&gt;\&amp;quot;&lt;/span&gt;&lt;span style="color:#ff1493"&gt;&amp;quot;&lt;/span&gt;filePath&lt;span style="color:#ff1493"&gt;&amp;quot;&lt;/span&gt;&lt;span style="color:#6a5acd"&gt;\&amp;quot;&lt;/span&gt; &lt;span style="color:#ff1493"&gt;&amp;amp;&amp;quot;&lt;/span&gt;)&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;This is an &lt;code&gt;awk&lt;/code&gt; script which mostly consists of handling different ways that the tag file can be structured. Since the &lt;a href="http://appliedabstraction.blogspot.com/2010/02/exploring-ctags-1-motivations.html"&gt;point&lt;/a&gt; is to provide a platform for experimenting with Ctags, it seems premature to commit to specific choices of absolute or relative paths, numeric line references or &lt;code&gt;ex&lt;/code&gt; patterns, extended fields from &lt;a href="http://ctags.sourceforge.net/"&gt;Exuberant Ctags&lt;/a&gt; or just vanilla Ctags lines. For what it is worth, I'm invoking Exuberant Ctags as &lt;code&gt;ctags -n --fields=+a+m+n+S -R&lt;/code&gt; (but there may well be better choices). &lt;br /&gt;&lt;br /&gt;At the end &lt;code&gt; open-tag-files&lt;/code&gt;, I use &lt;code&gt;altsee&lt;/code&gt; to open the source files. This is a &lt;a href="http://appliedabstraction.blogspot.com/2009/11/replacement-for-subethaedit-command.html"&gt;replacement&lt;/a&gt; for the &lt;code&gt;see&lt;/code&gt; command line tool that comes with SubEthaEdit. I find that &lt;code&gt;see&lt;/code&gt; is a bit of a hassle for this sort of use, so gave up on it for here (if you can get &lt;code&gt;open-tag-files&lt;/code&gt; to work cleanly with multiple selected files, I'd love to hear about how!). &lt;br /&gt;&lt;br /&gt;All the scripts and handlers need to be assembled into a compiled AppleScript in &lt;code&gt;~/Library/Application Support/SubEthaEdit/Scripts/&lt;/code&gt; with the shell scripts set to be executable and on the &lt;a href="http://appliedabstraction.blogspot.com/2010/02/ctags-in-subethaedit.html"&gt;path&lt;/a&gt; defined in the AppleScripts. If you're not sure where to put the shell scripts, I'd suggest creating a &lt;code&gt;~/Library/Application Support/SubEthaEdit/bin/&lt;/code&gt; directory for SubEthaEdit-related shell scripts, and putting the scripts there. A compiled script with the needed shell script support is available for &lt;a href="http://www.box.net/shared/zksls0d28x"&gt;download&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3164056879958238384-3569232555814016237?l=appliedabstraction.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://appliedabstraction.blogspot.com/feeds/3569232555814016237/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3164056879958238384&amp;postID=3569232555814016237' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/3569232555814016237'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/3569232555814016237'/><link rel='alternate' type='text/html' href='http://appliedabstraction.blogspot.com/2010/02/finding-definitions-with-ctags-in.html' title='Finding Definitions with Ctags in SubEthaEdit'/><author><name>Michael Barber</name><uri>http://www.blogger.com/profile/15713879666980145844</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3164056879958238384.post-8515606062940544067</id><published>2010-02-20T09:12:00.001-08:00</published><updated>2010-02-20T09:12:10.286-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ctags'/><category scheme='http://www.blogger.com/atom/ns#' term='AppleScript'/><category scheme='http://www.blogger.com/atom/ns#' term='SubEthaEdit'/><title type='text'>Text Completions with Ctags in SubEthaEdit</title><content type='html'>With the infrastructure set up &lt;a href="http://appliedabstraction.blogspot.com/2010/02/exploring-ctags-1-motivations.html"&gt;in&lt;/a&gt; &lt;a href="http://appliedabstraction.blogspot.com/2010/02/find-that-tags-file.html"&gt;the&lt;/a&gt; &lt;a href="http://appliedabstraction.blogspot.com/2010/02/tag-matching.html"&gt;last&lt;/a&gt; &lt;a href="http://appliedabstraction.blogspot.com/2010/02/ctags-in-subethaedit.html"&gt;few&lt;/a&gt; &lt;a href="http://appliedabstraction.blogspot.com/2010/02/ctags-from-subethaedit-to-shell.html"&gt;posts&lt;/a&gt;, it is now relatively easy to add Ctags-based text completions to &lt;a href="http://www.subethaedit.net"&gt;SubEthaEdit&lt;/a&gt; (SEE). We use the shell scripts and AppleScript handlers to locate the tag file, determine a search term, get a list of tags matching the search term, and put up a dialog to have the user pick a tag. The only thing we're missing is a handler to actually insert the selected tag. &lt;br /&gt;&lt;br /&gt;Here's a handler that does the job:&lt;pre style="color:#000000; background-color:#fff5ee; font-size:10pt; font-family:'Courier New';"&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;to&lt;/span&gt; insertCompletion &lt;span style="color:#2e8b57; font-weight:bold"&gt;of&lt;/span&gt; baseText by completionText&lt;br /&gt;  &lt;span style="color:#2929cc"&gt;-- assumes that the baseText is what was determined from the selection&lt;/span&gt;&lt;br /&gt;  &lt;span style="color:#2e8b57; font-weight:bold"&gt;set&lt;/span&gt; {startChar, nextChar} &lt;span style="color:#2e8b57; font-weight:bold"&gt;to&lt;/span&gt; selectionRange &lt;span style="color:#2e8b57; font-weight:bold"&gt;without&lt;/span&gt; extendingFront &lt;span style="color:#2e8b57; font-weight:bold"&gt;and&lt;/span&gt; extendingEnd&lt;br /&gt;  &lt;span style="color:#2e8b57; font-weight:bold"&gt;if the&lt;/span&gt; completionText &lt;span style="color:#2e8b57; font-weight:bold"&gt;does not&lt;/span&gt; &lt;span style="color:#696969; font-weight:bold"&gt;start&lt;/span&gt; &lt;span style="color:#2e8b57; font-weight:bold"&gt;with the&lt;/span&gt; baseText &lt;span style="color:#2e8b57; font-weight:bold"&gt;then&lt;/span&gt;&lt;br /&gt;    &lt;span style="color:#2e8b57; font-weight:bold"&gt;error&lt;/span&gt; &lt;span style="color:#ff1493"&gt;&amp;quot;Invalid completion&amp;quot;&lt;/span&gt;&lt;br /&gt;  &lt;span style="color:#2e8b57; font-weight:bold"&gt;end if&lt;/span&gt;&lt;br /&gt;  &lt;span style="color:#2e8b57; font-weight:bold"&gt;if&lt;/span&gt; &lt;span style="color:#696969; font-weight:bold"&gt;length&lt;/span&gt; &lt;span style="color:#2e8b57; font-weight:bold"&gt;of&lt;/span&gt; baseText &lt;span style="color:#2e8b57; font-weight:bold"&gt;is equal to&lt;/span&gt; &lt;span style="color:#696969; font-weight:bold"&gt;length&lt;/span&gt; &lt;span style="color:#2e8b57; font-weight:bold"&gt;of&lt;/span&gt; completionText &lt;span style="color:#2e8b57; font-weight:bold"&gt;then&lt;/span&gt;&lt;br /&gt;    &lt;span style="color:#2929cc"&gt;-- completion is the same as the existing text, just position the insertion point&lt;/span&gt;&lt;br /&gt;    setSelectionRange &lt;span style="color:#2e8b57; font-weight:bold"&gt;to&lt;/span&gt; nextChar&lt;br /&gt;  &lt;span style="color:#2e8b57; font-weight:bold"&gt;else if&lt;/span&gt; startChar &lt;span style="color:#2e8b57; font-weight:bold"&gt;is equal to&lt;/span&gt; nextChar &lt;span style="color:#2e8b57; font-weight:bold"&gt;then&lt;/span&gt;&lt;br /&gt;    &lt;span style="color:#2929cc"&gt;-- empty selection, search term was inferred and only the difference needs to be included&lt;/span&gt;&lt;br /&gt;    &lt;span style="color:#2e8b57; font-weight:bold"&gt;set&lt;/span&gt; completion &lt;span style="color:#2e8b57; font-weight:bold"&gt;to&lt;/span&gt; &lt;span style="color:#696969; font-weight:bold"&gt;characters&lt;/span&gt; (&lt;span style="color:#ff1493"&gt;1&lt;/span&gt; + (&lt;span style="color:#696969; font-weight:bold"&gt;length&lt;/span&gt; &lt;span style="color:#2e8b57; font-weight:bold"&gt;of&lt;/span&gt; baseText)) &lt;span style="color:#000069"&gt;through&lt;/span&gt; (&lt;span style="color:#696969; font-weight:bold"&gt;length&lt;/span&gt; &lt;span style="color:#2e8b57; font-weight:bold"&gt;of&lt;/span&gt; completionText) &lt;span style="color:#2e8b57; font-weight:bold"&gt;of&lt;/span&gt; completionText &lt;span style="color:#2e8b57; font-weight:bold"&gt;as&lt;/span&gt; &lt;span style="color:#696969; font-weight:bold"&gt;text&lt;/span&gt;&lt;br /&gt;    setSelectionText &lt;span style="color:#2e8b57; font-weight:bold"&gt;to&lt;/span&gt; completion&lt;br /&gt;    setSelectionRange &lt;span style="color:#2e8b57; font-weight:bold"&gt;to&lt;/span&gt; nextChar + (&lt;span style="color:#696969; font-weight:bold"&gt;length&lt;/span&gt; &lt;span style="color:#2e8b57; font-weight:bold"&gt;of&lt;/span&gt; completion)&lt;br /&gt;  &lt;span style="color:#2e8b57; font-weight:bold"&gt;else&lt;/span&gt;&lt;br /&gt;    &lt;span style="color:#2929cc"&gt;--text selected, just replace it&lt;/span&gt;&lt;br /&gt;    setSelectionText &lt;span style="color:#2e8b57; font-weight:bold"&gt;to&lt;/span&gt; completionText&lt;br /&gt;    setSelectionRange &lt;span style="color:#2e8b57; font-weight:bold"&gt;to&lt;/span&gt; startChar + (&lt;span style="color:#696969; font-weight:bold"&gt;length&lt;/span&gt; &lt;span style="color:#2e8b57; font-weight:bold"&gt;of&lt;/span&gt; completionText)&lt;br /&gt;  &lt;span style="color:#2e8b57; font-weight:bold"&gt;end if&lt;/span&gt;&lt;br /&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;end&lt;/span&gt; insertCompletion&lt;br /&gt;&lt;/pre&gt;The handler has parameters corresponding to the base text sought for in the tag file and to the selected tag. These two strings are used, along with the length of the selection in SEE, to determine exactly how much text to insert. It would have been possible to just use the SEE selection, without passing in the base text, but it would have required essentially repeating the entire process of determining the search term; I think the design could be improved here, but I can live with this for now. &lt;br /&gt;&lt;br /&gt;Using all these handlers, the logic for the text completion script is now expressible in a compact form: &lt;pre style="color:#000000; background-color:#fff5ee; font-size:10pt; font-family:'Courier New';"&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;try&lt;/span&gt;&lt;br /&gt;  &lt;span style="color:#000069"&gt;requireValidDocumentForCtags&lt;/span&gt;()&lt;br /&gt;  &lt;span style="color:#2e8b57; font-weight:bold"&gt;set&lt;/span&gt; tagfilepath &lt;span style="color:#2e8b57; font-weight:bold"&gt;to&lt;/span&gt; &lt;span style="color:#000069"&gt;findTagFile&lt;/span&gt;()&lt;br /&gt;  &lt;span style="color:#2e8b57; font-weight:bold"&gt;set&lt;/span&gt; searchTerm &lt;span style="color:#2e8b57; font-weight:bold"&gt;to&lt;/span&gt; determineSearchTerm &lt;span style="color:#2e8b57; font-weight:bold"&gt;without&lt;/span&gt; userIntervention&lt;br /&gt;  &lt;span style="color:#2929cc"&gt;--set taglist to (pipeMatches of searchTerm out of tagfilepath thru &amp;quot;awk -F\&amp;quot;\\t\&amp;quot; '{ print $1 }' | sort -u&amp;quot;)&lt;/span&gt;&lt;br /&gt;  &lt;span style="color:#2e8b57; font-weight:bold"&gt;set&lt;/span&gt; taglist &lt;span style="color:#2e8b57; font-weight:bold"&gt;to&lt;/span&gt; (pipeMatches &lt;span style="color:#2e8b57; font-weight:bold"&gt;of&lt;/span&gt; searchTerm out &lt;span style="color:#2e8b57; font-weight:bold"&gt;of&lt;/span&gt; tagfilepath thru &lt;span style="color:#ff1493"&gt;&amp;quot;cut -f1 | sort -u&amp;quot;&lt;/span&gt;)&lt;br /&gt;  &lt;span style="color:#2e8b57; font-weight:bold"&gt;set&lt;/span&gt; selectedTag &lt;span style="color:#2e8b57; font-weight:bold"&gt;to&lt;/span&gt; (pickTags &lt;span style="color:#2e8b57; font-weight:bold"&gt;from&lt;/span&gt; taglist &lt;span style="color:#2e8b57; font-weight:bold"&gt;without&lt;/span&gt; multipleSelectionsAllowed)&lt;br /&gt;  insertCompletion &lt;span style="color:#2e8b57; font-weight:bold"&gt;of&lt;/span&gt; searchTerm by selectedTag&lt;br /&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;on error&lt;/span&gt; errMsg &lt;span style="color:#696969; font-weight:bold"&gt;number&lt;/span&gt; errNum&lt;br /&gt;  &lt;span style="color:#2e8b57; font-weight:bold"&gt;if&lt;/span&gt; errNum &lt;span style="color:#2e8b57; font-weight:bold"&gt;is equal to&lt;/span&gt; &lt;span style="color:#ff1493"&gt;901&lt;/span&gt; &lt;span style="color:#2e8b57; font-weight:bold"&gt;then&lt;/span&gt;&lt;br /&gt;    &lt;span style="color:#2e8b57; font-weight:bold"&gt;return&lt;/span&gt;&lt;br /&gt;  &lt;span style="color:#2e8b57; font-weight:bold"&gt;else if&lt;/span&gt; errNum &lt;span style="color:#2e8b57; font-weight:bold"&gt;is equal to&lt;/span&gt; &lt;span style="color:#ff1493"&gt;902&lt;/span&gt; &lt;span style="color:#2e8b57; font-weight:bold"&gt;then&lt;/span&gt;&lt;br /&gt;    beep&lt;br /&gt;    &lt;span style="color:#2e8b57; font-weight:bold"&gt;return&lt;/span&gt;&lt;br /&gt;  &lt;span style="color:#2e8b57; font-weight:bold"&gt;else&lt;/span&gt;&lt;br /&gt;    &lt;span style="color:#2e8b57; font-weight:bold"&gt;error&lt;/span&gt; errMsg &lt;span style="color:#696969; font-weight:bold"&gt;number&lt;/span&gt; errNum&lt;br /&gt;  &lt;span style="color:#2e8b57; font-weight:bold"&gt;end if&lt;/span&gt;&lt;br /&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;end try&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;The &lt;code&gt;try&lt;/code&gt; block catches the &lt;a href="http://appliedabstraction.blogspot.com/2010/02/ctags-in-subethaedit.html"&gt;errors we defined&lt;/a&gt;, letting any others go through for SEE to inform us about.&lt;br /&gt;&lt;br /&gt;The last component needed is a &lt;code&gt;seescriptsettings&lt;/code&gt; handler. I used this:&lt;pre style="color:#000000; background-color:#fff5ee; font-size:10pt; font-family:'Courier New';"&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;on&lt;/span&gt; &lt;span style="color:#000069"&gt;seescriptsettings&lt;/span&gt;()&lt;br /&gt;  {displayName:&lt;span style="color:#ff1493"&gt;&amp;quot;Complete using Ctags&amp;quot;&lt;/span&gt;, shortDisplayName:&lt;span style="color:#ff1493"&gt;&amp;quot;Ctags Completion&amp;quot;&lt;/span&gt;, keyboardShortcut:&lt;span style="color:#ff1493"&gt;&amp;quot;&amp;#64;^t&amp;quot;&lt;/span&gt;, inContextMenu:&lt;span style="color:#ff1493"&gt;&amp;quot;yes&amp;quot;&lt;/span&gt;}&lt;br /&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;end&lt;/span&gt; seescriptsettings&lt;br /&gt;&lt;/pre&gt;All this needs to be assembled into a script, which is saved as a compiled script in &lt;code&gt;~/Library/Application Support/SubEthaEdit/Scripts/&lt;/code&gt;. A compiled script is available for &lt;a href="http://www.box.net/shared/zksls0d28x"&gt;download&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3164056879958238384-8515606062940544067?l=appliedabstraction.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://appliedabstraction.blogspot.com/feeds/8515606062940544067/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3164056879958238384&amp;postID=8515606062940544067' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/8515606062940544067'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/8515606062940544067'/><link rel='alternate' type='text/html' href='http://appliedabstraction.blogspot.com/2010/02/text-completions-with-ctags-in.html' title='Text Completions with Ctags in SubEthaEdit'/><author><name>Michael Barber</name><uri>http://www.blogger.com/profile/15713879666980145844</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3164056879958238384.post-32463658325540626</id><published>2010-02-18T13:00:00.001-08:00</published><updated>2010-02-18T13:06:17.191-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ctags'/><category scheme='http://www.blogger.com/atom/ns#' term='AppleScript'/><category scheme='http://www.blogger.com/atom/ns#' term='SubEthaEdit'/><category scheme='http://www.blogger.com/atom/ns#' term='shell'/><title type='text'>Ctags from SubEthaEdit to the Shell</title><content type='html'>In the last few posts on Ctags, I've presented shell scripts for &lt;a href="http://appliedabstraction.blogspot.com/2010/02/find-that-tags-file.html"&gt;locating a tag file&lt;/a&gt; and &lt;a href="http://appliedabstraction.blogspot.com/2010/02/tag-matching.html"&gt;looking up a tag&lt;/a&gt; in it, and &lt;a href="http://appliedabstraction.blogspot.com/2010/02/ctags-in-subethaedit.html"&gt;AppleScripts for identifying what tag file should be used and what tag to search for in it.&lt;/a&gt; In this post, I'll present AppleScript handlers that bridge between these two scripting systems. As in the &lt;a href="http://appliedabstraction.blogspot.com/2010/02/ctags-in-subethaedit.html"&gt;previous post&lt;/a&gt;, I'll use my &lt;a href="http://appliedabstraction.blogspot.com/2008/01/seeing-latex-19-subethaedittools-or.html"&gt;SubEthaEditTools&lt;/a&gt; to simplify the process. &lt;br /&gt;&lt;br /&gt;Essentially, the handler will need to construct a shell command that invokes &lt;code&gt;look&lt;/code&gt; to find a tag in the tag file. Beyond that, I'll include the option to post-process the matching lines, which I'll &lt;a href="http://appliedabstraction.blogspot.com/2010/02/tag-matching.html"&gt;use for text completion&lt;/a&gt;. For finding the definition of a tag, no post-processing is needed, so the handler checks for an empty pipeline and handles it cleanly. &lt;br /&gt;&lt;br /&gt;The handler is:&lt;pre style="color:#000000; background-color:#fff5ee; font-size:10pt; font-family:'Courier New';"&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;to&lt;/span&gt; pipeMatches &lt;span style="color:#2e8b57; font-weight:bold"&gt;of&lt;/span&gt; tag out &lt;span style="color:#2e8b57; font-weight:bold"&gt;of&lt;/span&gt; tagfile thru pipeline&lt;br /&gt;  &lt;span style="color:#2e8b57; font-weight:bold"&gt;ignoring&lt;/span&gt; white space&lt;br /&gt;    &lt;span style="color:#2e8b57; font-weight:bold"&gt;if&lt;/span&gt; &lt;span style="color:#ff1493"&gt;&amp;quot;&amp;quot;&lt;/span&gt; &lt;span style="color:#2e8b57; font-weight:bold"&gt;is equal to&lt;/span&gt; pipeline &lt;span style="color:#2e8b57; font-weight:bold"&gt;then&lt;/span&gt;&lt;br /&gt;      &lt;span style="color:#2e8b57; font-weight:bold"&gt;set&lt;/span&gt; postProcess &lt;span style="color:#2e8b57; font-weight:bold"&gt;to&lt;/span&gt; &lt;span style="color:#ff1493"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;br /&gt;    &lt;span style="color:#2e8b57; font-weight:bold"&gt;else&lt;/span&gt;&lt;br /&gt;      &lt;span style="color:#2e8b57; font-weight:bold"&gt;set&lt;/span&gt; postProcess &lt;span style="color:#2e8b57; font-weight:bold"&gt;to&lt;/span&gt; &lt;span style="color:#ff1493"&gt;&amp;quot;| &amp;quot;&lt;/span&gt; &amp;amp; pipeline&lt;br /&gt;    &lt;span style="color:#2e8b57; font-weight:bold"&gt;end if&lt;/span&gt;&lt;br /&gt;  &lt;span style="color:#2e8b57; font-weight:bold"&gt;end ignoring&lt;/span&gt;&lt;br /&gt;  &lt;span style="color:#2e8b57; font-weight:bold"&gt;set&lt;/span&gt; lookupScript &lt;span style="color:#2e8b57; font-weight:bold"&gt;to&lt;/span&gt; (join &lt;span style="color:#2e8b57; font-weight:bold"&gt;of&lt;/span&gt; {UnixPath, &lt;span style="color:#ff1493"&gt;&amp;quot;look &amp;quot;&lt;/span&gt;, tag, quoted form &lt;span style="color:#2e8b57; font-weight:bold"&gt;of&lt;/span&gt; tagfile, postProcess} by space)&lt;br /&gt;  &lt;span style="color:#2e8b57; font-weight:bold"&gt;try&lt;/span&gt;&lt;br /&gt;    do shell &lt;span style="color:#2e8b57; font-weight:bold"&gt;script&lt;/span&gt; lookupScript&lt;br /&gt;  &lt;span style="color:#2e8b57; font-weight:bold"&gt;on error&lt;/span&gt;&lt;br /&gt;    &lt;span style="color:#2e8b57; font-weight:bold"&gt;error&lt;/span&gt; &lt;span style="color:#ff1493"&gt;&amp;quot;Pipeline failed to process tag matches&amp;quot;&lt;/span&gt; &lt;span style="color:#696969; font-weight:bold"&gt;number&lt;/span&gt; &lt;span style="color:#ff1493"&gt;902&lt;/span&gt;&lt;br /&gt;  &lt;span style="color:#2e8b57; font-weight:bold"&gt;end try&lt;/span&gt;&lt;br /&gt;  paragraphs &lt;span style="color:#2e8b57; font-weight:bold"&gt;of the&lt;/span&gt; result&lt;br /&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;end&lt;/span&gt; pipeMatches&lt;br /&gt;&lt;/pre&gt;Note that the handler ends by taking the paragraphs of the shell script result. This converts the lines selected by &lt;code&gt;look&lt;/code&gt; (and any post-processing) into a list of matches.&lt;br /&gt;&lt;br /&gt;With the &lt;a href="http://appliedabstraction.blogspot.com/2010/02/exploring-ctags-1-motivations.html"&gt;two use cases&lt;/a&gt; in mind, the user will need to pick a relevant tag or tags from the list of matches. With text completion, only one selection makes sense, but more than one might be OK for finding definitions. Here's a handler for the two cases:&lt;pre style="color:#000000; background-color:#fff5ee; font-size:10pt; font-family:'Courier New';"&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;to&lt;/span&gt; pickTags &lt;span style="color:#2e8b57; font-weight:bold"&gt;from&lt;/span&gt; taglist given multipleSelectionsAllowed:allowMultiple&lt;br /&gt;  &lt;span style="color:#2e8b57; font-weight:bold"&gt;try&lt;/span&gt;&lt;br /&gt;    &lt;span style="color:#2e8b57; font-weight:bold"&gt;if&lt;/span&gt; allowMultiple &lt;span style="color:#2e8b57; font-weight:bold"&gt;then&lt;/span&gt;&lt;br /&gt;      &lt;span style="color:#696969; font-weight:bold"&gt;choose&lt;/span&gt; &lt;span style="color:#2e8b57; font-weight:bold"&gt;from&lt;/span&gt; &lt;span style="color:#696969; font-weight:bold"&gt;list&lt;/span&gt; taglist &lt;span style="color:#2e8b57; font-weight:bold"&gt;with&lt;/span&gt; &lt;span style="color:#696969; font-weight:bold"&gt;title&lt;/span&gt; &lt;span style="color:#ff1493"&gt;&amp;quot;Matching tags&amp;quot;&lt;/span&gt; &lt;span style="color:#2e8b57; font-weight:bold"&gt;with&lt;/span&gt; prompt &lt;span style="color:#ff1493"&gt;&amp;quot;Select tag:&amp;quot;&lt;/span&gt; &lt;span style="color:#696969; font-weight:bold"&gt;default items&lt;/span&gt; (&lt;span style="color:#2e8b57; font-weight:bold"&gt;first&lt;/span&gt; &lt;span style="color:#696969; font-weight:bold"&gt;item&lt;/span&gt; &lt;span style="color:#2e8b57; font-weight:bold"&gt;of&lt;/span&gt; taglist) &lt;span style="color:#2e8b57; font-weight:bold"&gt;with&lt;/span&gt; multiple selections allowed&lt;br /&gt;      join &lt;span style="color:#2e8b57; font-weight:bold"&gt;of&lt;/span&gt; result by &lt;span style="color:#ff1493"&gt;&amp;quot;&lt;/span&gt;&lt;span style="color:#6a5acd"&gt;\n&lt;/span&gt;&lt;span style="color:#ff1493"&gt;&amp;quot;&lt;/span&gt;&lt;br /&gt;    &lt;span style="color:#2e8b57; font-weight:bold"&gt;else&lt;/span&gt;&lt;br /&gt;      &lt;span style="color:#696969; font-weight:bold"&gt;choose&lt;/span&gt; &lt;span style="color:#2e8b57; font-weight:bold"&gt;from&lt;/span&gt; &lt;span style="color:#696969; font-weight:bold"&gt;list&lt;/span&gt; taglist &lt;span style="color:#2e8b57; font-weight:bold"&gt;with&lt;/span&gt; &lt;span style="color:#696969; font-weight:bold"&gt;title&lt;/span&gt; &lt;span style="color:#ff1493"&gt;&amp;quot;Matching tags&amp;quot;&lt;/span&gt; &lt;span style="color:#2e8b57; font-weight:bold"&gt;with&lt;/span&gt; prompt &lt;span style="color:#ff1493"&gt;&amp;quot;Select tag:&amp;quot;&lt;/span&gt; &lt;span style="color:#696969; font-weight:bold"&gt;default items&lt;/span&gt; (&lt;span style="color:#2e8b57; font-weight:bold"&gt;first&lt;/span&gt; &lt;span style="color:#696969; font-weight:bold"&gt;item&lt;/span&gt; &lt;span style="color:#2e8b57; font-weight:bold"&gt;of&lt;/span&gt; taglist)&lt;br /&gt;      &lt;span style="color:#2e8b57; font-weight:bold"&gt;first&lt;/span&gt; &lt;span style="color:#696969; font-weight:bold"&gt;item&lt;/span&gt; &lt;span style="color:#2e8b57; font-weight:bold"&gt;of the&lt;/span&gt; result&lt;br /&gt;    &lt;span style="color:#2e8b57; font-weight:bold"&gt;end if&lt;/span&gt;&lt;br /&gt;  &lt;span style="color:#2e8b57; font-weight:bold"&gt;on error&lt;/span&gt;&lt;br /&gt;    &lt;span style="color:#2929cc"&gt;-- user canceled, do nothing&lt;/span&gt;&lt;br /&gt;    &lt;span style="color:#2e8b57; font-weight:bold"&gt;error&lt;/span&gt; &lt;span style="color:#696969; font-weight:bold"&gt;number&lt;/span&gt; &lt;span style="color:#ff1493"&gt;901&lt;/span&gt;&lt;br /&gt;  &lt;span style="color:#2e8b57; font-weight:bold"&gt;end try&lt;/span&gt;&lt;br /&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;end&lt;/span&gt; pickTags&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;We're nearly done. What remains is to assemble all these handlers into AppleScripts for the two use cases, adding whatever specifics are needed for the two tasks. &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3164056879958238384-32463658325540626?l=appliedabstraction.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://appliedabstraction.blogspot.com/feeds/32463658325540626/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3164056879958238384&amp;postID=32463658325540626' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/32463658325540626'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/32463658325540626'/><link rel='alternate' type='text/html' href='http://appliedabstraction.blogspot.com/2010/02/ctags-from-subethaedit-to-shell.html' title='Ctags from SubEthaEdit to the Shell'/><author><name>Michael Barber</name><uri>http://www.blogger.com/profile/15713879666980145844</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3164056879958238384.post-1279457187723610418</id><published>2010-02-17T11:38:00.001-08:00</published><updated>2010-02-18T13:04:54.931-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ctags'/><category scheme='http://www.blogger.com/atom/ns#' term='AppleScript'/><category scheme='http://www.blogger.com/atom/ns#' term='SubEthaEdit'/><category scheme='http://www.blogger.com/atom/ns#' term='Mac OS X'/><title type='text'>Ctags in SubEthaEdit</title><content type='html'>We've now looked at how to &lt;a href="http://appliedabstraction.blogspot.com/2010/02/find-that-tags-file.html"&gt;locate the right tags file&lt;/a&gt; and &lt;a href="http://appliedabstraction.blogspot.com/2010/02/tag-matching.html"&gt;match a tag&lt;/a&gt; against it by working in the shell. But our &lt;a href="http://appliedabstraction.blogspot.com/2010/02/exploring-ctags-1-motivations.html"&gt;goal&lt;/a&gt; is to connect Ctags to an editor, &lt;a href="http://www.subethaedit.net/"&gt;SubEthaEdit&lt;/a&gt; (SEE) in this case. We thus will need to switch from the world of the shell to the world of AppleScript. In this post, I'll just focus on getting the path to the tags file and a tag for which to search from SEE. &lt;br /&gt;&lt;br /&gt;I'll not be working directly with SubEthaEdit's AppleScript dictionary, instead using my &lt;a href="http://appliedabstraction.blogspot.com/2008/01/seeing-latex-19-subethaedittools-or.html"&gt;SubEthaEditTools&lt;/a&gt; handlers as a basis. Should anyone be interested in connecting Ctags to another Mac OS X editor that supports AppleScript, it would probably be better to port the SubEthaEditTools handlers to work with the editor and directly use the scripts I'll present here.&lt;br /&gt;&lt;br /&gt;As a general design strategy, I'll identify two AppleScript error numbers with expected behaviors. First, I'll use number 901 to indicate that tag processing should be abandoned. Second, I'll use number 902 to indicate that an error of known type has occurred. This lets me handle a broad class of troubles by either quietly exiting, or beeping then exiting. Any other errors will just be unhandled, causing SubEthaEdit to show a sheet with details of the error. &lt;br /&gt;&lt;br /&gt;Additionally, I'll need to define a search path for shell tools. Rather than using a &lt;a href="http://appliedabstraction.blogspot.com/2007/10/seeing-latex-13-accessing-environment.html"&gt;customizable environment&lt;/a&gt; as I've done before, I'll just define one as an AppleScript property:&lt;pre style="color:#000000; background-color:#fff5ee; font-size:10pt; font-family:'Courier New';"&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;property&lt;/span&gt; UnixPath : &lt;span style="color:#ff1493"&gt;&amp;quot;export PATH=&lt;/span&gt;&lt;span style="color:#6a5acd"&gt;\&amp;quot;&lt;/span&gt;&lt;span style="color:#ff1493"&gt;$HOME/Library/Application Support/SubEthaEdit/bin:/Library/Application Support/SubEthaEdit/bin:$HOME/Library/bin:/usr/local/bin:/opt/local/bin:/usr/bin:/bin:/usr/local/sbin:/opt/local/sbin:/usr/sbin:/sbin&lt;/span&gt;&lt;span style="color:#6a5acd"&gt;\&amp;quot;&lt;/span&gt;&lt;span style="color:#ff1493"&gt;;&amp;quot;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;To find the tags file, I first need to make sure a document is available to use as the starting point for the search. Second, I just need to call out to the shell with an appropriate command. Encapsulating these in handlers, I define:&lt;pre style="color:#000000; background-color:#fff5ee; font-size:10pt; font-family:'Courier New';"&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;on&lt;/span&gt; &lt;span style="color:#000069"&gt;requireValidDocumentForCtags&lt;/span&gt;()&lt;br /&gt;  &lt;span style="color:#2e8b57; font-weight:bold"&gt;if not&lt;/span&gt; &lt;span style="color:#000069"&gt;documentIsAvailable&lt;/span&gt;() &lt;span style="color:#2e8b57; font-weight:bold"&gt;then&lt;/span&gt;&lt;br /&gt;    &lt;span style="color:#2e8b57; font-weight:bold"&gt;error&lt;/span&gt; &lt;span style="color:#ff1493"&gt;&amp;quot;No document open&amp;quot;&lt;/span&gt; &lt;span style="color:#696969; font-weight:bold"&gt;number&lt;/span&gt; &lt;span style="color:#ff1493"&gt;902&lt;/span&gt;&lt;br /&gt;  &lt;span style="color:#2e8b57; font-weight:bold"&gt;end if&lt;/span&gt;&lt;br /&gt;  checkSaveStatus &lt;span style="color:#2e8b57; font-weight:bold"&gt;without&lt;/span&gt; updating&lt;br /&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;end&lt;/span&gt; requireValidDocumentForCtags&lt;br /&gt;&lt;br /&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;to&lt;/span&gt; &lt;span style="color:#000069"&gt;findTagFile&lt;/span&gt;()&lt;br /&gt;  &lt;span style="color:#2e8b57; font-weight:bold"&gt;set&lt;/span&gt; findTagfileScript &lt;span style="color:#2e8b57; font-weight:bold"&gt;to&lt;/span&gt; (join &lt;span style="color:#2e8b57; font-weight:bold"&gt;of&lt;/span&gt; {UnixPath, &lt;span style="color:#ff1493"&gt;&amp;quot;climb&amp;quot;&lt;/span&gt;, &lt;span style="color:#ff1493"&gt;&amp;quot;-b&lt;/span&gt; &lt;span style="color:#6a5acd"&gt;\&amp;quot;&lt;/span&gt;&lt;span style="color:#ff1493"&gt;$(dirname&amp;quot;&lt;/span&gt;, quoted form &lt;span style="color:#2e8b57; font-weight:bold"&gt;of&lt;/span&gt; &lt;span style="color:#000069"&gt;documentPath&lt;/span&gt;(), &lt;span style="color:#ff1493"&gt;&amp;quot;)&lt;/span&gt;&lt;span style="color:#6a5acd"&gt;\&amp;quot;&lt;/span&gt;&lt;span style="color:#ff1493"&gt;&amp;quot;&lt;/span&gt;, &lt;span style="color:#ff1493"&gt;&amp;quot;tags&amp;quot;&lt;/span&gt;} by space)&lt;br /&gt;  &lt;span style="color:#2e8b57; font-weight:bold"&gt;try&lt;/span&gt;&lt;br /&gt;    do shell &lt;span style="color:#2e8b57; font-weight:bold"&gt;script&lt;/span&gt; findTagfileScript&lt;br /&gt;  &lt;span style="color:#2e8b57; font-weight:bold"&gt;on error&lt;/span&gt;&lt;br /&gt;    &lt;span style="color:#2e8b57; font-weight:bold"&gt;error&lt;/span&gt; &lt;span style="color:#ff1493"&gt;&amp;quot;Unable to locate tags file&amp;quot;&lt;/span&gt;&lt;br /&gt;  &lt;span style="color:#2e8b57; font-weight:bold"&gt;end try&lt;/span&gt;&lt;br /&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;end&lt;/span&gt; findTagFile&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Getting the candidate tag is harder than getting the path to the tag file, mostly because it is not as well-defined of a task. Since Ctags can index lots of different languages, it won't be easy to get a solution that is right for every language. Instead, I'll define a handler that works reasonably for a lot of languages, and maintains the possibility for the user to specify the candidate precisely. This latter case is straightforward: if there is text selected in SEE, we'll search for that tag. &lt;br /&gt;&lt;br /&gt;When no text is selected, we need to get a candidate tag in some other way. To me, it makes sense that finding symbol definitions should let the user give a term in a dialog, and that text completion should work by using the text preceding the cursor. But how much text should be used? I don't think that the longest possible tag makes sense, as that would mean, e.g., a method invocation in Python of form &lt;code&gt;obj.method&lt;/code&gt; would use the whole thing, even though that full term is unlikely to be indexed in the tag file. Instead, it would be better to just use &lt;code&gt;method&lt;/code&gt; as the candidate tag. A reasonable choice for many languages would then be to take the longest string of alphanumeric characters and underscores, right to left from the insertion point. Those choices lead to the handler:&lt;pre style="color:#000000; background-color:#fff5ee; font-size:10pt; font-family:'Courier New';"&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;to&lt;/span&gt; determineSearchTerm given userIntervention:shouldAsk&lt;br /&gt;  &lt;span style="color:#2e8b57; font-weight:bold"&gt;set&lt;/span&gt; {startChar, nextChar} &lt;span style="color:#2e8b57; font-weight:bold"&gt;to&lt;/span&gt; selectionRange &lt;span style="color:#2e8b57; font-weight:bold"&gt;without&lt;/span&gt; extendingFront &lt;span style="color:#2e8b57; font-weight:bold"&gt;and&lt;/span&gt; extendingEnd&lt;br /&gt;  &lt;span style="color:#2e8b57; font-weight:bold"&gt;if&lt;/span&gt; startChar &lt;span style="color:#2e8b57; font-weight:bold"&gt;is equal to&lt;/span&gt; nextChar &lt;span style="color:#2e8b57; font-weight:bold"&gt;then&lt;/span&gt;&lt;br /&gt;    &lt;span style="color:#2929cc"&gt;-- empty selection&lt;/span&gt;&lt;br /&gt;    &lt;span style="color:#2e8b57; font-weight:bold"&gt;if&lt;/span&gt; shouldAsk &lt;span style="color:#2e8b57; font-weight:bold"&gt;then&lt;/span&gt;&lt;br /&gt;      &lt;span style="color:#2e8b57; font-weight:bold"&gt;try&lt;/span&gt;&lt;br /&gt;        &lt;span style="color:#696969; font-weight:bold"&gt;display dialog&lt;/span&gt; &lt;span style="color:#ff1493"&gt;&amp;quot;Enter search term:&amp;quot;&lt;/span&gt; &lt;span style="color:#696969; font-weight:bold"&gt;default&lt;/span&gt; answer &lt;span style="color:#ff1493"&gt;&amp;quot;&amp;quot;&lt;/span&gt; &lt;span style="color:#2e8b57; font-weight:bold"&gt;with&lt;/span&gt; &lt;span style="color:#696969; font-weight:bold"&gt;title&lt;/span&gt; &lt;span style="color:#ff1493"&gt;&amp;quot;Find Definition&amp;quot;&lt;/span&gt;&lt;br /&gt;      &lt;span style="color:#2e8b57; font-weight:bold"&gt;on error&lt;/span&gt; &lt;span style="color:#696969; font-weight:bold"&gt;number&lt;/span&gt; -&lt;span style="color:#ff1493"&gt;128&lt;/span&gt;&lt;br /&gt;        &lt;span style="color:#2e8b57; font-weight:bold"&gt;error&lt;/span&gt; &lt;span style="color:#ff1493"&gt;&amp;quot;User canceled&amp;quot;&lt;/span&gt; &lt;span style="color:#696969; font-weight:bold"&gt;number&lt;/span&gt; &lt;span style="color:#ff1493"&gt;901&lt;/span&gt;&lt;br /&gt;      &lt;span style="color:#2e8b57; font-weight:bold"&gt;end try&lt;/span&gt;&lt;br /&gt;      &lt;span style="color:#696969; font-weight:bold"&gt;text&lt;/span&gt; returned &lt;span style="color:#2e8b57; font-weight:bold"&gt;of&lt;/span&gt; result&lt;br /&gt;    &lt;span style="color:#2e8b57; font-weight:bold"&gt;else&lt;/span&gt;&lt;br /&gt;      &lt;span style="color:#2929cc"&gt;-- try the whole line&lt;/span&gt;&lt;br /&gt;      &lt;span style="color:#2e8b57; font-weight:bold"&gt;set&lt;/span&gt; selectionContents &lt;span style="color:#2e8b57; font-weight:bold"&gt;to&lt;/span&gt; extendedSelectionText &lt;span style="color:#2e8b57; font-weight:bold"&gt;with&lt;/span&gt; extendingFront &lt;span style="color:#2e8b57; font-weight:bold"&gt;without&lt;/span&gt; extendingEnd&lt;br /&gt;      &lt;span style="color:#2e8b57; font-weight:bold"&gt;get&lt;/span&gt; shellTransform &lt;span style="color:#2e8b57; font-weight:bold"&gt;of the&lt;/span&gt; selectionContents &lt;span style="color:#696969; font-weight:bold"&gt;for&lt;/span&gt; &lt;span style="color:#ff1493"&gt;&amp;quot;&amp;quot;&lt;/span&gt; thru &lt;span style="color:#ff1493"&gt;&amp;quot;sed -E -e 's/.*([[:&amp;lt;:]][[:alnum:]_]+)$/&lt;/span&gt;&lt;span style="color:#6a5acd"&gt;\\&lt;/span&gt;&lt;span style="color:#ff1493"&gt;1/'&amp;quot;&lt;/span&gt; &lt;span style="color:#2e8b57; font-weight:bold"&gt;without&lt;/span&gt; alteringLineEndings&lt;br /&gt;      &lt;span style="color:#2929cc"&gt;-- sed returns lines that are terminated with linefeeds, so get text before the final linefeed&lt;/span&gt;&lt;br /&gt;      paragraph -&lt;span style="color:#ff1493"&gt;2&lt;/span&gt; &lt;span style="color:#2e8b57; font-weight:bold"&gt;of the&lt;/span&gt; result&lt;br /&gt;    &lt;span style="color:#2e8b57; font-weight:bold"&gt;end if&lt;/span&gt;&lt;br /&gt;  &lt;span style="color:#2e8b57; font-weight:bold"&gt;else&lt;/span&gt;&lt;br /&gt;    &lt;span style="color:#2929cc"&gt;-- just use the selection; there is too much variation in what could be a tag to guess&lt;/span&gt;&lt;br /&gt;    &lt;span style="color:#000069"&gt;selectionText&lt;/span&gt;()&lt;br /&gt;  &lt;span style="color:#2e8b57; font-weight:bold"&gt;end if&lt;/span&gt;&lt;br /&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;end&lt;/span&gt; determineSearchTerm&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The handlers presented in this post are enough to get the path to the tag file and a (partial) tag to search for. Next time, I'll connect these values from SubEthaEdit to the shell scripts handling the lookup. &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3164056879958238384-1279457187723610418?l=appliedabstraction.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://appliedabstraction.blogspot.com/feeds/1279457187723610418/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3164056879958238384&amp;postID=1279457187723610418' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/1279457187723610418'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/1279457187723610418'/><link rel='alternate' type='text/html' href='http://appliedabstraction.blogspot.com/2010/02/ctags-in-subethaedit.html' title='Ctags in SubEthaEdit'/><author><name>Michael Barber</name><uri>http://www.blogger.com/profile/15713879666980145844</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3164056879958238384.post-8627350870317146498</id><published>2010-02-14T04:32:00.001-08:00</published><updated>2010-02-14T04:32:06.427-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ctags'/><category scheme='http://www.blogger.com/atom/ns#' term='shell'/><title type='text'>Tag Matching</title><content type='html'>Our goal remains to &lt;a href="http://appliedabstraction.blogspot.com/2010/02/exploring-ctags-1-motivations.html"&gt;add support for Ctags to an application.&lt;/a&gt; We know how to &lt;a href="http://appliedabstraction.blogspot.com/2010/02/find-that-tags-file.html"&gt;locate the relevant tags file,&lt;/a&gt; but what do we do with it? Fundamentally, we use the tag file to match identifiers against tags indexed by Ctags; let's make that specific, restricting ourselves for the moment to just working in the shell.&lt;br /&gt;&lt;br /&gt;The tag file is structured as sorted lines of tab-separated records. The first field in the line is the tag, other fields identify the position of the tag in a particular source file. With this, we can check a candidate tag &lt;code&gt;$TAG&lt;/code&gt; against the tag file &lt;code&gt;$TAGFILE&lt;/code&gt; using &lt;code&gt;look&lt;/code&gt;:&lt;pre&gt;&lt;br /&gt;look "$TAG" "$TAGFILE"&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Easy and fast.&lt;br /&gt;&lt;br /&gt;To use tags to find the definition of a symbol, we'll want to hang onto all the information about each matching tag; the above use of &lt;code&gt;look&lt;/code&gt; is all we need. For use in text completion, we'll want a longer pipeline eliminating extraneous information:&lt;pre&gt;&lt;br /&gt;look "$TAG" "$TAGFILE" | cut -f1 | sort -u&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The pipeline drops all fields but the first, the tag field, using &lt;code&gt;cut&lt;/code&gt; and eliminates duplicates with &lt;code&gt;sort -u&lt;/code&gt; (I suspect that &lt;code&gt;uniq&lt;/code&gt; should work here, but &lt;code&gt;look&lt;/code&gt; is curiously unspecific about whether it always produces its output in sorted order). &lt;br /&gt;&lt;br /&gt;And that's it for matching tags. The file format was clearly set up with just this sort of use in mind. More details on the file format &lt;a href="http://ctags.sourceforge.net/FORMAT"&gt;are&lt;/a&gt; &lt;a href="http://en.wikipedia.org/wiki/Ctags#Ctags_and_Exuberant_Ctags"&gt;available&lt;/a&gt; &lt;a href="http://www.opengroup.org/onlinepubs/009695399/utilities/ctags.html"&gt;elsewhere&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3164056879958238384-8627350870317146498?l=appliedabstraction.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://appliedabstraction.blogspot.com/feeds/8627350870317146498/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3164056879958238384&amp;postID=8627350870317146498' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/8627350870317146498'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/8627350870317146498'/><link rel='alternate' type='text/html' href='http://appliedabstraction.blogspot.com/2010/02/tag-matching.html' title='Tag Matching'/><author><name>Michael Barber</name><uri>http://www.blogger.com/profile/15713879666980145844</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3164056879958238384.post-4719014950609045025</id><published>2010-02-14T00:20:00.001-08:00</published><updated>2010-02-14T03:58:14.648-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ctags'/><category scheme='http://www.blogger.com/atom/ns#' term='shell'/><title type='text'>Find that Tags File!</title><content type='html'>Our first challenge in &lt;a href="http://appliedabstraction.blogspot.com/2010/02/exploring-ctags-1-motivations.html"&gt;incorporating Ctags into an editor&lt;/a&gt; is locating the tags file. A first attempt might be to look for a file named &lt;em&gt;tags&lt;/em&gt; in the same directory as the document in the frontmost editor window. But this isn't quite good enough. Ctags can create a tags file by recursively descending into subdirectories, so a useful tags file might be located somewhere higher in the directory tree. &lt;br /&gt;&lt;br /&gt;It seems like there should be a standard shell command to search upward in the directory tree, but I couldn't find it. The task isn't really that hard, so I wrote a shell script &lt;code&gt;climb&lt;/code&gt; to do it instead of spending more time fruitlessly searching. Usage is patterned after &lt;code&gt;which&lt;/code&gt;. To look for a tags file that recursively indexed the present directory, just do &lt;code&gt;climb tags&lt;/code&gt;. Options are available to set where the search starts and stops.&lt;br /&gt;&lt;br /&gt;Here's my script:&lt;br /&gt;&lt;pre style="color:#000000; background-color:#fff5ee; font-size:10pt; font-family:'Courier New';"&gt;&lt;span style="color:#2929cc"&gt;#!/bin/sh&lt;/span&gt;&lt;br /&gt;&lt;span style="color:#2929cc"&gt;#&lt;/span&gt;&lt;br /&gt;&lt;span style="color:#2929cc"&gt;# climb -- locate a file by ascending the directory tree&lt;/span&gt;&lt;br /&gt;&lt;span style="color:#2929cc"&gt;#&lt;/span&gt;&lt;br /&gt;&lt;span style="color:#2929cc"&gt;# climb [-b bottomdir] [-t topdir] filename&lt;/span&gt;&lt;br /&gt;&lt;span style="color:#2929cc"&gt;#&lt;/span&gt;&lt;br /&gt;&lt;span style="color:#2929cc"&gt;# Climb directory tree looking for a file named filename. The search&lt;/span&gt;&lt;br /&gt;&lt;span style="color:#2929cc"&gt;# starts by checking in the bottom directory (defaults to the current&lt;/span&gt;&lt;br /&gt;&lt;span style="color:#2929cc"&gt;# directory), with each parent directory checked until either the&lt;/span&gt;&lt;br /&gt;&lt;span style="color:#2929cc"&gt;# file is found or the top directory (defaults to root) is reached.&lt;/span&gt;&lt;br /&gt;&lt;span style="color:#2929cc"&gt;#&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color:#2929cc"&gt;# Options allow setting the search range. Defaults are starting the&lt;/span&gt;&lt;br /&gt;&lt;span style="color:#2929cc"&gt;# search in the current directory and ending at root.&lt;/span&gt;&lt;br /&gt;upTo&lt;span style="color:#676767"&gt;=&lt;/span&gt;&lt;span style="color:#ff1493"&gt;&amp;quot;/&amp;quot;&lt;/span&gt;&lt;br /&gt;upFrom&lt;span style="color:#676767"&gt;=&lt;/span&gt;&lt;span style="color:#ff1493"&gt;&amp;quot;$PWD&amp;quot;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;while&lt;/span&gt; &lt;span style="color:#2e8b57; font-weight:bold"&gt;getopts&lt;/span&gt; b&lt;span style="color:#676767"&gt;:&lt;/span&gt;t&lt;span style="color:#676767"&gt;:&lt;/span&gt; opt&lt;br /&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;do&lt;/span&gt;&lt;br /&gt;    &lt;span style="color:#2e8b57; font-weight:bold"&gt;case&lt;/span&gt; &lt;span style="color:#2e8b57; font-weight:bold"&gt;$opt&lt;/span&gt; &lt;span style="color:#2e8b57; font-weight:bold"&gt;in&lt;/span&gt;&lt;br /&gt;    b&lt;span style="color:#676767"&gt;)&lt;/span&gt;  upFrom&lt;span style="color:#676767"&gt;=&lt;/span&gt;&lt;span style="color:#ff1493"&gt;&amp;quot;$OPTARG&amp;quot;&lt;/span&gt;&lt;br /&gt;        &lt;span style="color:#2e8b57; font-weight:bold"&gt;if&lt;/span&gt; &lt;span style="color:#676767"&gt;! [ -&lt;/span&gt;d &lt;span style="color:#ff1493"&gt;&amp;quot;$upFrom&amp;quot;&lt;/span&gt; &lt;span style="color:#676767"&gt;]&lt;/span&gt;&lt;br /&gt;        &lt;span style="color:#2e8b57; font-weight:bold"&gt;then&lt;/span&gt;&lt;br /&gt;            &lt;span style="color:#2e8b57; font-weight:bold"&gt;echo $0&lt;/span&gt;&lt;span style="color:#676767"&gt;:&lt;/span&gt; &lt;span style="color:#2e8b57; font-weight:bold"&gt;$upFrom&lt;/span&gt;&lt;span style="color:#676767"&gt;:&lt;/span&gt; No such directory &lt;span style="color:#676767"&gt;&amp;gt;&amp;amp;&lt;/span&gt;&lt;span style="color:#ff1493"&gt;2&lt;/span&gt;&lt;br /&gt;            &lt;span style="color:#2e8b57; font-weight:bold"&gt;exit&lt;/span&gt; &lt;span style="color:#ff1493"&gt;2&lt;/span&gt;&lt;br /&gt;		&lt;span style="color:#2e8b57; font-weight:bold"&gt;else&lt;/span&gt;&lt;br /&gt;			&lt;span style="color:#2929cc"&gt;# standardize the lowermost directory path&lt;/span&gt;&lt;br /&gt;        	upFrom&lt;span style="color:#676767"&gt;=&lt;/span&gt;&lt;span style="color:#ff1493"&gt;&amp;quot;$(cd &amp;quot;&lt;/span&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;$upFrom&lt;/span&gt;&lt;span style="color:#ff1493"&gt;&amp;quot; &amp;amp;&amp;amp; pwd -P)&amp;quot;&lt;/span&gt;&lt;br /&gt;        &lt;span style="color:#2e8b57; font-weight:bold"&gt;fi&lt;/span&gt;&lt;br /&gt;        &lt;span style="color:#676767"&gt;;;&lt;/span&gt;&lt;br /&gt;    t&lt;span style="color:#676767"&gt;)&lt;/span&gt;  upTo&lt;span style="color:#676767"&gt;=&lt;/span&gt;&lt;span style="color:#ff1493"&gt;&amp;quot;$OPTARG&amp;quot;&lt;/span&gt;&lt;br /&gt;        &lt;span style="color:#2e8b57; font-weight:bold"&gt;if&lt;/span&gt; &lt;span style="color:#676767"&gt;! [ -&lt;/span&gt;d &lt;span style="color:#ff1493"&gt;&amp;quot;$upTo&amp;quot;&lt;/span&gt; &lt;span style="color:#676767"&gt;]&lt;/span&gt;&lt;br /&gt;        &lt;span style="color:#2e8b57; font-weight:bold"&gt;then&lt;/span&gt;&lt;br /&gt;            &lt;span style="color:#2e8b57; font-weight:bold"&gt;echo $0&lt;/span&gt;&lt;span style="color:#676767"&gt;:&lt;/span&gt; &lt;span style="color:#2e8b57; font-weight:bold"&gt;$upTo&lt;/span&gt;&lt;span style="color:#676767"&gt;:&lt;/span&gt; No such directory &lt;span style="color:#676767"&gt;&amp;gt;&amp;amp;&lt;/span&gt;&lt;span style="color:#ff1493"&gt;2&lt;/span&gt;&lt;br /&gt;            &lt;span style="color:#2e8b57; font-weight:bold"&gt;exit&lt;/span&gt; &lt;span style="color:#ff1493"&gt;2&lt;/span&gt;&lt;br /&gt;		&lt;span style="color:#2e8b57; font-weight:bold"&gt;else&lt;/span&gt;&lt;br /&gt;			&lt;span style="color:#2929cc"&gt;# standardize the uppermost directory path&lt;/span&gt;&lt;br /&gt;        	upTo&lt;span style="color:#676767"&gt;=&lt;/span&gt;&lt;span style="color:#ff1493"&gt;&amp;quot;$(cd &amp;quot;&lt;/span&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;$upTo&lt;/span&gt;&lt;span style="color:#ff1493"&gt;&amp;quot; &amp;amp;&amp;amp; pwd -P)&amp;quot;&lt;/span&gt;&lt;br /&gt;        &lt;span style="color:#2e8b57; font-weight:bold"&gt;fi&lt;/span&gt;&lt;br /&gt;        &lt;span style="color:#676767"&gt;;;&lt;/span&gt;&lt;br /&gt;    &lt;span style="color:#2e8b57; font-weight:bold"&gt;esac&lt;/span&gt;&lt;br /&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;done&lt;/span&gt;&lt;br /&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;shift&lt;/span&gt; $&lt;span style="color:#676767"&gt;((&lt;/span&gt;OPTIND &lt;span style="color:#676767"&gt;-&lt;/span&gt; &lt;span style="color:#ff1493"&gt;1&lt;/span&gt;&lt;span style="color:#676767"&gt;))&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;targetFile&lt;span style="color:#676767"&gt;=&lt;/span&gt;&lt;span style="color:#ff1493"&gt;&amp;quot;$1&amp;quot;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color:#2929cc"&gt;# To ensure termination, require that the uppermost directory is&lt;/span&gt;&lt;br /&gt;&lt;span style="color:#2929cc"&gt;# an ancestor of the directory where the search begins.&lt;/span&gt;&lt;br /&gt;indx&lt;span style="color:#676767"&gt;=&lt;/span&gt;$&lt;span style="color:#676767"&gt;(&lt;/span&gt;&lt;span style="color:#696969; font-weight:bold"&gt;awk&lt;/span&gt; &lt;span style="color:#676767"&gt;-&lt;/span&gt;v d1&lt;span style="color:#676767"&gt;=&lt;/span&gt;&lt;span style="color:#ff1493"&gt;&amp;quot;$upTo&amp;quot;&lt;/span&gt; &lt;span style="color:#676767"&gt;-&lt;/span&gt;v d2&lt;span style="color:#676767"&gt;=&lt;/span&gt;&lt;span style="color:#ff1493"&gt;&amp;quot;$upFrom&amp;quot;&lt;/span&gt; &lt;span style="color:#ff1493"&gt;'BEGIN { print index(d2, d1) }'&lt;/span&gt;&lt;span style="color:#676767"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;if&lt;/span&gt; &lt;span style="color:#676767"&gt;! [&lt;/span&gt; &lt;span style="color:#2e8b57; font-weight:bold"&gt;$indx&lt;/span&gt; &lt;span style="color:#676767"&gt;-&lt;/span&gt;eq &lt;span style="color:#ff1493"&gt;1&lt;/span&gt; &lt;span style="color:#676767"&gt;]&lt;/span&gt;&lt;br /&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;then&lt;/span&gt;&lt;br /&gt;    &lt;span style="color:#2e8b57; font-weight:bold"&gt;echo $0&lt;/span&gt;&lt;span style="color:#676767"&gt;:&lt;/span&gt; &lt;span style="color:#2e8b57; font-weight:bold"&gt;$upFrom&lt;/span&gt; is not a descendant of &lt;span style="color:#2e8b57; font-weight:bold"&gt;$upTo&lt;/span&gt; &lt;span style="color:#676767"&gt;&amp;gt;&amp;amp;&lt;/span&gt;&lt;span style="color:#ff1493"&gt;2&lt;/span&gt;&lt;br /&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;fi&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color:#2929cc"&gt;# Check each directory for the target file, moving up the directory tree&lt;/span&gt;&lt;br /&gt;&lt;span style="color:#2929cc"&gt;# until either the target is found or the uppermost directory has been&lt;/span&gt;&lt;br /&gt;&lt;span style="color:#2929cc"&gt;# searched. Both the lowermost directory and the uppermost directory&lt;/span&gt;&lt;br /&gt;&lt;span style="color:#2929cc"&gt;# are checked for the file.&lt;/span&gt;&lt;br /&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;while&lt;/span&gt; true&lt;br /&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;do&lt;/span&gt;&lt;br /&gt;    &lt;span style="color:#2e8b57; font-weight:bold"&gt;if&lt;/span&gt; &lt;span style="color:#676767"&gt;[ -&lt;/span&gt;f &lt;span style="color:#ff1493"&gt;&amp;quot;$upFrom/$targetFile&amp;quot;&lt;/span&gt; &lt;span style="color:#676767"&gt;]&lt;/span&gt;&lt;br /&gt;    &lt;span style="color:#2e8b57; font-weight:bold"&gt;then&lt;/span&gt;&lt;br /&gt;        &lt;span style="color:#2e8b57; font-weight:bold"&gt;break&lt;/span&gt;&lt;br /&gt;    &lt;span style="color:#2e8b57; font-weight:bold"&gt;fi&lt;/span&gt;&lt;br /&gt;    &lt;span style="color:#2e8b57; font-weight:bold"&gt;if&lt;/span&gt; &lt;span style="color:#676767"&gt;[&lt;/span&gt; &lt;span style="color:#ff1493"&gt;&amp;quot;X$upTo&amp;quot;&lt;/span&gt; &lt;span style="color:#676767"&gt;=&lt;/span&gt; &lt;span style="color:#ff1493"&gt;&amp;quot;X$upFrom&amp;quot;&lt;/span&gt; &lt;span style="color:#676767"&gt;] || [ -&lt;/span&gt;z &lt;span style="color:#ff1493"&gt;&amp;quot;$upFrom&amp;quot;&lt;/span&gt; &lt;span style="color:#676767"&gt;] || [&lt;/span&gt; &lt;span style="color:#ff1493"&gt;&amp;quot;X$upFrom&amp;quot;&lt;/span&gt; &lt;span style="color:#676767"&gt;=&lt;/span&gt; &lt;span style="color:#ff1493"&gt;&amp;quot;X/&amp;quot;&lt;/span&gt; &lt;span style="color:#676767"&gt;]&lt;/span&gt;&lt;br /&gt;    &lt;span style="color:#2e8b57; font-weight:bold"&gt;then&lt;/span&gt;&lt;br /&gt;        &lt;span style="color:#2e8b57; font-weight:bold"&gt;exit&lt;/span&gt; &lt;span style="color:#ff1493"&gt;1&lt;/span&gt;&lt;br /&gt;    &lt;span style="color:#2e8b57; font-weight:bold"&gt;else&lt;/span&gt;&lt;br /&gt;        upFrom&lt;span style="color:#676767"&gt;=&lt;/span&gt;$&lt;span style="color:#676767"&gt;(&lt;/span&gt;&lt;span style="color:#696969; font-weight:bold"&gt;dirname&lt;/span&gt; &lt;span style="color:#ff1493"&gt;&amp;quot;$upFrom&amp;quot;&lt;/span&gt;&lt;span style="color:#676767"&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span style="color:#2e8b57; font-weight:bold"&gt;fi&lt;/span&gt;&lt;br /&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;done&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;echo&lt;/span&gt; &lt;span style="color:#ff1493"&gt;&amp;quot;$upFrom/$targetFile&amp;quot;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Most of the script deals with establishing the starting and ending points of the search, which I referred to in the script as the bottommost and topmost directories, respectively. They're put into a standardized format and tested for consistency, then used to define the search. The search is simple, amounting to nothing more than successively chopping off the last element of the directory path and seeing if the target file is in the resulting directory. The search stops when the topmost directory is reached, or when root is reached, just in case.&lt;br /&gt;&lt;br /&gt;The script is general purpose, suitable for finding more than just tags files. I have mostly just called &lt;code&gt;climb&lt;/code&gt; from AppleScripts in SubEthaEdit, with a pretty well-behaved file name and start directory. It may well be that more complex use would reveal bugs, so use with caution.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3164056879958238384-4719014950609045025?l=appliedabstraction.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://appliedabstraction.blogspot.com/feeds/4719014950609045025/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3164056879958238384&amp;postID=4719014950609045025' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/4719014950609045025'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/4719014950609045025'/><link rel='alternate' type='text/html' href='http://appliedabstraction.blogspot.com/2010/02/find-that-tags-file.html' title='Find that Tags File!'/><author><name>Michael Barber</name><uri>http://www.blogger.com/profile/15713879666980145844</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3164056879958238384.post-5335923840538500151</id><published>2010-02-13T11:23:00.001-08:00</published><updated>2010-02-13T22:37:48.515-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ctags'/><category scheme='http://www.blogger.com/atom/ns#' term='AppleScript'/><category scheme='http://www.blogger.com/atom/ns#' term='SubEthaEdit'/><category scheme='http://www.blogger.com/atom/ns#' term='shell'/><title type='text'>Exploring Ctags: Motivations</title><content type='html'>I've been vaguely aware of Ctags for years, but only in the last few months have I gotten a handle on how it would benefit me. Part of the problem is that most mentions of Ctags seem to assume you already know the benefits: the &lt;a href="http://en.wikipedia.org/wiki/Ctags"&gt;Wikipedia entry&lt;/a&gt; does this, as does the &lt;a href="http://ctags.sourceforge.net/"&gt;Exuberant Ctags&lt;/a&gt; site. Worse, many discussions make it seem that it is just an auxiliary for &lt;code&gt;vi&lt;/code&gt;-family editors, so perhaps not even relevant to those who, like me, haven't seriously used a &lt;code&gt;vi&lt;/code&gt; derivative in years.&lt;br /&gt;&lt;br /&gt;After seeing an &lt;a href="http://www.entropy.ch/blog/Mac+OS+X/2009/04/02/Better-BBEdit-Completion-With-ctags.html"&gt;explanation in the context of BBEdit&lt;/a&gt;, I have a much better idea of what Ctags provides. Essentially, it generates an index called a &lt;em&gt;tags file&lt;/em&gt; that allows for easier code navigation across multiple files, in particular providing text completions and navigating to the definition of functions or other symbols. Within BBEdit, tags also are used to improve syntax highlighting. &lt;br /&gt;&lt;br /&gt;I must admit that I find some of the praise for it to be overblown, but maybe I just need to try it. Of course, I don't use BBEdit, either. In fact, no editor that I regularly use supports Ctags. Let's do something about that. I'll work in the context of &lt;a href="http://www.subethaedit.net/"&gt;SubEthaEdit&lt;/a&gt; (SEE), since I have a &lt;a href="http://appliedabstraction.blogspot.com/search/label/SubEthaEdit"&gt;fair amount of experience with scripting it&lt;/a&gt;, and of &lt;a href="http://ctags.sourceforge.net/"&gt;Exuberant Ctags&lt;/a&gt;, since it supports more languages than the Ctags built into Mac OS X. &lt;br /&gt;&lt;br /&gt;I'll add two features to SEE, text completion and finding definitions. To some extent, these are redundant, in that SEE has text completions and a function pop-up, but they don't extend across multiple files in the same way as Ctags. I won't be able to do anything with syntax highlighting, as in BBEdit, but it should still be enough to try out Ctags.&lt;br /&gt;&lt;br /&gt;Both features will be structured as AppleScripts invoking shell scripts to do most of the work. The AppleScripts both have a similar structure, consisting of: &lt;ol&gt;&lt;br /&gt;&lt;li&gt;locating the tags file&lt;/li&gt;&lt;br /&gt;&lt;li&gt;determining a search term to match against the tags file&lt;/li&gt;&lt;br /&gt;&lt;li&gt;identifying and processing matching tags&lt;/li&gt;&lt;br /&gt;&lt;li&gt;letting the user select from the matching tags&lt;/li&gt;&lt;br /&gt;&lt;li&gt;doing something with the selection &lt;/li&gt;&lt;br /&gt;&lt;/ol&gt;&lt;br /&gt;I'll break these stages out into several posts.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3164056879958238384-5335923840538500151?l=appliedabstraction.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://appliedabstraction.blogspot.com/feeds/5335923840538500151/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3164056879958238384&amp;postID=5335923840538500151' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/5335923840538500151'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/5335923840538500151'/><link rel='alternate' type='text/html' href='http://appliedabstraction.blogspot.com/2010/02/exploring-ctags-1-motivations.html' title='Exploring Ctags: Motivations'/><author><name>Michael Barber</name><uri>http://www.blogger.com/profile/15713879666980145844</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3164056879958238384.post-8046686468466618526</id><published>2010-02-12T22:46:00.001-08:00</published><updated>2010-02-13T02:21:17.436-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='time management'/><category scheme='http://www.blogger.com/atom/ns#' term='AppleScript'/><title type='text'>DWM AppleScripts</title><content type='html'>I've been experimenting with new time management systems from &lt;a href="http://www.markforster.net/"&gt;Mark Forster&lt;/a&gt;, first trying &lt;a href="http://www.markforster.net/blog/2009/9/5/preliminary-instructions-for-autofocus-v-4.html"&gt;Autofocus v. 4&lt;/a&gt; and now &lt;a href="http://www.markforster.net/blog/2010/2/1/dit2-af5-who-cares-what-its-called-this-is-what-im-working-o.html"&gt;DWM&lt;/a&gt;. I've found AF4 to be quite nice over the last few weeks, and like what I've seen of DWM over the last few days. In each case, I've used iCal to manage the tasks in the system.&lt;br /&gt;&lt;br /&gt;With DWM, I keep my at-home tasks as iCal todos on a separate calendar (my work tasks are still in AF4, but will be switched over soon). Each todo has a due date; the due date here doesn't mean "do on this date," but instead means "do by this date." I keep the tasks sorted by due date. For tasks that really must be done on a particular date, put them on a different calendar, and they'll appear at the top of the list on that due date. This works well, but it is a little annoying to regularly set the due dates by hand. &lt;br /&gt;&lt;br /&gt;The todos are set with a regular pattern, to either the next week or the next month. This is scriptable. Here is the next-week script, which I saved as "To Do Within 7 Days" under the iCal application scripts: &lt;br /&gt;&lt;pre style="color:#000000; background-color:#fff5ee; font-size:10pt; font-family:'Courier New';"&gt;setDueDate &lt;span style="color:#2e8b57; font-weight:bold"&gt;of&lt;/span&gt; (&lt;span style="color:#ff1493"&gt;7&lt;/span&gt;*days) &lt;span style="color:#696969; font-weight:bold"&gt;for&lt;/span&gt; &lt;span style="color:#000069"&gt;selectedToDo&lt;/span&gt;()&lt;br /&gt;&lt;br /&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;to&lt;/span&gt; setDueDate &lt;span style="color:#2e8b57; font-weight:bold"&gt;of&lt;/span&gt; timeFrame &lt;span style="color:#696969; font-weight:bold"&gt;for&lt;/span&gt; task&lt;br /&gt;	&lt;span style="color:#2e8b57; font-weight:bold"&gt;set&lt;/span&gt; newDate &lt;span style="color:#2e8b57; font-weight:bold"&gt;to&lt;/span&gt; (&lt;span style="color:#696969; font-weight:bold"&gt;current date&lt;/span&gt;) + timeFrame&lt;br /&gt;	&lt;span style="color:#2e8b57; font-weight:bold"&gt;tell&lt;/span&gt; &lt;span style="color:#696969; font-weight:bold"&gt;application&lt;/span&gt; &lt;span style="color:#ff1493"&gt;&amp;quot;iCal&amp;quot;&lt;/span&gt; &lt;span style="color:#2e8b57; font-weight:bold"&gt;to set&lt;/span&gt; due &lt;span style="color:#696969; font-weight:bold"&gt;date&lt;/span&gt; &lt;span style="color:#2e8b57; font-weight:bold"&gt;of&lt;/span&gt; task &lt;span style="color:#2e8b57; font-weight:bold"&gt;to&lt;/span&gt; newDate&lt;br /&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;end&lt;/span&gt; setDueDate&lt;br /&gt;&lt;br /&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;on&lt;/span&gt; &lt;span style="color:#000069"&gt;selectedToDo&lt;/span&gt;()&lt;br /&gt;	&lt;span style="color:#2e8b57; font-weight:bold"&gt;set&lt;/span&gt; referenceText &lt;span style="color:#2e8b57; font-weight:bold"&gt;to&lt;/span&gt; iCalSelectionText &lt;span style="color:#2e8b57; font-weight:bold"&gt;at&lt;/span&gt; &lt;span style="color:#ff1493"&gt;1&lt;/span&gt;&lt;br /&gt;	&lt;span style="color:#2e8b57; font-weight:bold"&gt;tell&lt;/span&gt; &lt;span style="color:#696969; font-weight:bold"&gt;application&lt;/span&gt; &lt;span style="color:#ff1493"&gt;&amp;quot;iCal&amp;quot;&lt;/span&gt;&lt;br /&gt;		&lt;span style="color:#2e8b57; font-weight:bold"&gt;repeat with&lt;/span&gt; cal &lt;span style="color:#2e8b57; font-weight:bold"&gt;in&lt;/span&gt; calendars&lt;br /&gt;			&lt;span style="color:#2e8b57; font-weight:bold"&gt;set&lt;/span&gt; matches &lt;span style="color:#2e8b57; font-weight:bold"&gt;to&lt;/span&gt; (todos &lt;span style="color:#2e8b57; font-weight:bold"&gt;of&lt;/span&gt; cal where summary &lt;span style="color:#2e8b57; font-weight:bold"&gt;is equal to&lt;/span&gt; referenceText)&lt;br /&gt;			&lt;span style="color:#2e8b57; font-weight:bold"&gt;if&lt;/span&gt; (&lt;span style="color:#696969; font-weight:bold"&gt;count&lt;/span&gt; &lt;span style="color:#2e8b57; font-weight:bold"&gt;of&lt;/span&gt; matches) &amp;gt; &lt;span style="color:#ff1493"&gt;0&lt;/span&gt; &lt;span style="color:#2e8b57; font-weight:bold"&gt;then&lt;/span&gt;&lt;br /&gt;				&lt;span style="color:#2e8b57; font-weight:bold"&gt;exit repeat&lt;/span&gt;&lt;br /&gt;			&lt;span style="color:#2e8b57; font-weight:bold"&gt;end if&lt;/span&gt;&lt;br /&gt;		&lt;span style="color:#2e8b57; font-weight:bold"&gt;end repeat&lt;/span&gt;&lt;br /&gt;		&lt;span style="color:#2e8b57; font-weight:bold"&gt;if&lt;/span&gt; (&lt;span style="color:#696969; font-weight:bold"&gt;count&lt;/span&gt; &lt;span style="color:#2e8b57; font-weight:bold"&gt;of&lt;/span&gt; matches) &lt;span style="color:#2e8b57; font-weight:bold"&gt;is equal to&lt;/span&gt; &lt;span style="color:#ff1493"&gt;0&lt;/span&gt; &lt;span style="color:#2e8b57; font-weight:bold"&gt;then&lt;/span&gt;&lt;br /&gt;			&lt;span style="color:#2e8b57; font-weight:bold"&gt;error&lt;/span&gt; &lt;span style="color:#ff1493"&gt;&amp;quot;No matching to-do item found.&amp;quot;&lt;/span&gt;&lt;br /&gt;		&lt;span style="color:#2e8b57; font-weight:bold"&gt;end if&lt;/span&gt;&lt;br /&gt;		&lt;span style="color:#2e8b57; font-weight:bold"&gt;first&lt;/span&gt; &lt;span style="color:#696969; font-weight:bold"&gt;item&lt;/span&gt; &lt;span style="color:#2e8b57; font-weight:bold"&gt;of&lt;/span&gt; matches&lt;br /&gt;	&lt;span style="color:#2e8b57; font-weight:bold"&gt;end tell&lt;/span&gt;&lt;br /&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;end&lt;/span&gt; selectedToDo&lt;br /&gt;&lt;br /&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;on&lt;/span&gt; iCalSelectionText &lt;span style="color:#2e8b57; font-weight:bold"&gt;at&lt;/span&gt; timeDelay&lt;br /&gt;	&lt;span style="color:#2e8b57; font-weight:bold"&gt;set the&lt;/span&gt; oldClipboard &lt;span style="color:#2e8b57; font-weight:bold"&gt;to the&lt;/span&gt; clipboard&lt;br /&gt;	&lt;span style="color:#2e8b57; font-weight:bold"&gt;try&lt;/span&gt;&lt;br /&gt;		copyICalSelection &lt;span style="color:#2e8b57; font-weight:bold"&gt;at&lt;/span&gt; timeDelay&lt;br /&gt;		&lt;span style="color:#2e8b57; font-weight:bold"&gt;set&lt;/span&gt; selectionText &lt;span style="color:#2e8b57; font-weight:bold"&gt;to the&lt;/span&gt; clipboard&lt;br /&gt;	&lt;span style="color:#2e8b57; font-weight:bold"&gt;on error&lt;/span&gt; errText &lt;span style="color:#696969; font-weight:bold"&gt;number&lt;/span&gt; errNum&lt;br /&gt;		&lt;span style="color:#2e8b57; font-weight:bold"&gt;set the&lt;/span&gt; clipboard &lt;span style="color:#2e8b57; font-weight:bold"&gt;to the&lt;/span&gt; oldClipboard&lt;br /&gt;		&lt;span style="color:#2e8b57; font-weight:bold"&gt;error&lt;/span&gt; errText &lt;span style="color:#696969; font-weight:bold"&gt;number&lt;/span&gt; errNum&lt;br /&gt;	&lt;span style="color:#2e8b57; font-weight:bold"&gt;end try&lt;/span&gt;&lt;br /&gt;	&lt;span style="color:#2e8b57; font-weight:bold"&gt;set the&lt;/span&gt; clipboard &lt;span style="color:#2e8b57; font-weight:bold"&gt;to the&lt;/span&gt; oldClipboard&lt;br /&gt;	selectionText&lt;br /&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;end&lt;/span&gt; iCalSelectionText&lt;br /&gt;&lt;br /&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;on&lt;/span&gt; copyICalSelection &lt;span style="color:#2e8b57; font-weight:bold"&gt;at&lt;/span&gt; timeDelay&lt;br /&gt;	&lt;span style="color:#2e8b57; font-weight:bold"&gt;tell&lt;/span&gt; &lt;span style="color:#696969; font-weight:bold"&gt;application&lt;/span&gt; &lt;span style="color:#ff1493"&gt;&amp;quot;iCal&amp;quot;&lt;/span&gt; &lt;span style="color:#2e8b57; font-weight:bold"&gt;to&lt;/span&gt; &lt;span style="color:#696969; font-weight:bold"&gt;activate&lt;/span&gt;&lt;br /&gt;	&lt;span style="color:#2e8b57; font-weight:bold"&gt;tell&lt;/span&gt; &lt;span style="color:#696969; font-weight:bold"&gt;application&lt;/span&gt; &lt;span style="color:#ff1493"&gt;&amp;quot;System Events&amp;quot;&lt;/span&gt;&lt;br /&gt;		&lt;span style="color:#2e8b57; font-weight:bold"&gt;tell&lt;/span&gt; &lt;span style="color:#696969; font-weight:bold"&gt;process&lt;/span&gt; &lt;span style="color:#ff1493"&gt;&amp;quot;iCal&amp;quot;&lt;/span&gt;&lt;br /&gt;			keystroke &lt;span style="color:#2e8b57; font-weight:bold"&gt;return&lt;/span&gt;&lt;br /&gt;			keystroke &lt;span style="color:#ff1493"&gt;&amp;quot;c&amp;quot;&lt;/span&gt; using {command down}&lt;br /&gt;			keystroke &lt;span style="color:#2e8b57; font-weight:bold"&gt;return&lt;/span&gt;&lt;br /&gt;		&lt;span style="color:#2e8b57; font-weight:bold"&gt;end tell&lt;/span&gt;&lt;br /&gt;	&lt;span style="color:#2e8b57; font-weight:bold"&gt;end tell&lt;/span&gt;&lt;br /&gt;	&lt;span style="color:#696969; font-weight:bold"&gt;delay&lt;/span&gt; timeDelay&lt;br /&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;end&lt;/span&gt; copyICalSelection&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The next-month script is similar, just replace the 7*days by 30*days. &lt;br /&gt;&lt;br /&gt;The bulk of the script, and the only thing tricky about it, is getting a selected to-do item; the iCal scripting dictionary provides no way to do this! The handlers &lt;em&gt;selectedToDo&lt;/em&gt;, &lt;em&gt;iCalSelectionText&lt;/em&gt;, and &lt;em&gt;copyICalSelection&lt;/em&gt; are a work around. I didn't come up with this approach, it comes from a &lt;a href="http://www.macosxhints.com/article.php?story=20071208001422690"&gt;Mac OS X Hints&lt;/a&gt; contributor.&lt;br /&gt;&lt;br /&gt;Overall, I'm liking DWM a lot, but I doubt I'd like it without the scripts. Because of the nature of the system, I'll make no recommendation either for or against using DWM until a month has passed, but I already do think it is quite interesting and worth taking a look at.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Update:&lt;/strong&gt; You can download compiled scripts &lt;a href="http://www.box.net/shared/485np4i6bk"&gt;here&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3164056879958238384-8046686468466618526?l=appliedabstraction.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://appliedabstraction.blogspot.com/feeds/8046686468466618526/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3164056879958238384&amp;postID=8046686468466618526' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/8046686468466618526'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/8046686468466618526'/><link rel='alternate' type='text/html' href='http://appliedabstraction.blogspot.com/2010/02/dwm-applescripts.html' title='DWM AppleScripts'/><author><name>Michael Barber</name><uri>http://www.blogger.com/profile/15713879666980145844</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3164056879958238384.post-1653914993805981122</id><published>2009-11-06T09:49:00.001-08:00</published><updated>2009-11-06T09:49:29.004-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='AppleScript'/><category scheme='http://www.blogger.com/atom/ns#' term='SubEthaEdit'/><category scheme='http://www.blogger.com/atom/ns#' term='shell'/><title type='text'>Replacement for SubEthaEdit's Command Line Tool</title><content type='html'>The &lt;code&gt;see&lt;/code&gt; command line tool for SubEthaEdit makes scripting needlessly complex. Because it tries to write the contents of the document to &lt;code&gt;stdout&lt;/code&gt; upon close of the document, you wind up having to jump through hoops to get sensible behavior. The end result is that it is easy to write an AppleScript where SubEthaEdit calls out to the shell, but hard to have the shell communicate back to SubEthaEdit.&lt;br /&gt;&lt;br /&gt;Here, I present an alternative. It is a shell script that uses &lt;code&gt;osascript&lt;/code&gt; to open a document in SubEthaEdit. Optionally, a specific line can be given, and, if UI scripting is enabled, the document will be scrolled to show the line. &lt;br /&gt;&lt;pre style="color:#000000; background-color:#fff5ee; font-size:10pt; font-family:'Courier New';"&gt;&lt;span style="color:#2929cc"&gt;#!/bin/sh&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;PROGRAM&lt;span style="color:#676767"&gt;=&lt;/span&gt;$&lt;span style="color:#676767"&gt;(&lt;/span&gt;&lt;span style="color:#696969; font-weight:bold"&gt;basename&lt;/span&gt; &lt;span style="color:#2e8b57; font-weight:bold"&gt;$0&lt;/span&gt;&lt;span style="color:#676767"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;usage&lt;span style="color:#676767"&gt;()&lt;/span&gt;&lt;br /&gt;&lt;span style="color:#676767"&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span style="color:#2e8b57; font-weight:bold"&gt;echo&lt;/span&gt; &lt;span style="color:#ff1493"&gt;&amp;quot;Usage: $PROGRAM [-gh] filename&amp;quot;&lt;/span&gt;&lt;br /&gt;&lt;span style="color:#676767"&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;lineGiven&lt;span style="color:#676767"&gt;=&lt;/span&gt;false&lt;br /&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;while&lt;/span&gt; &lt;span style="color:#2e8b57; font-weight:bold"&gt;getopts&lt;/span&gt; &lt;span style="color:#676767"&gt;:&lt;/span&gt;g&lt;span style="color:#676767"&gt;:&lt;/span&gt;h opt&lt;br /&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;do&lt;/span&gt;&lt;br /&gt;    &lt;span style="color:#2e8b57; font-weight:bold"&gt;case&lt;/span&gt; &lt;span style="color:#2e8b57; font-weight:bold"&gt;$opt&lt;/span&gt; &lt;span style="color:#2e8b57; font-weight:bold"&gt;in&lt;/span&gt;&lt;br /&gt;    g&lt;span style="color:#676767"&gt;)&lt;/span&gt;      lineGiven&lt;span style="color:#676767"&gt;=&lt;/span&gt;true&lt;br /&gt;            lineToShow&lt;span style="color:#676767"&gt;=&lt;/span&gt;&lt;span style="color:#ff1493"&gt;&amp;quot;$OPTARG&amp;quot;&lt;/span&gt;&lt;br /&gt;            &lt;span style="color:#676767"&gt;;;&lt;/span&gt;&lt;br /&gt;    h&lt;span style="color:#676767"&gt;)&lt;/span&gt;      usage&lt;br /&gt;            &lt;span style="color:#2e8b57; font-weight:bold"&gt;exit&lt;/span&gt; &lt;span style="color:#ff1493"&gt;0&lt;/span&gt;&lt;br /&gt;            &lt;span style="color:#676767"&gt;;;&lt;/span&gt;&lt;br /&gt;    &lt;span style="color:#ff1493"&gt;'?'&lt;/span&gt;&lt;span style="color:#676767"&gt;)&lt;/span&gt;    &lt;span style="color:#2e8b57; font-weight:bold"&gt;echo&lt;/span&gt; &lt;span style="color:#ff1493"&gt;&amp;quot;$PROGRAM: invalid option -$OPTARG&amp;quot;&lt;/span&gt; &lt;span style="color:#676767"&gt;&amp;gt;&amp;amp;&lt;/span&gt;&lt;span style="color:#ff1493"&gt;2&lt;/span&gt;&lt;br /&gt;            usage &lt;span style="color:#676767"&gt;&amp;gt;&amp;amp;&lt;/span&gt;&lt;span style="color:#ff1493"&gt;2&lt;/span&gt;&lt;br /&gt;            &lt;span style="color:#2e8b57; font-weight:bold"&gt;exit&lt;/span&gt; &lt;span style="color:#ff1493"&gt;1&lt;/span&gt;&lt;br /&gt;            &lt;span style="color:#676767"&gt;;;&lt;/span&gt;&lt;br /&gt;    &lt;span style="color:#2e8b57; font-weight:bold"&gt;esac&lt;/span&gt;&lt;br /&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;done&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;shift&lt;/span&gt; $&lt;span style="color:#676767"&gt;((&lt;/span&gt;OPTIND &lt;span style="color:#676767"&gt;-&lt;/span&gt; &lt;span style="color:#ff1493"&gt;1&lt;/span&gt;&lt;span style="color:#676767"&gt;))&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;fileName&lt;span style="color:#676767"&gt;=&lt;/span&gt;&lt;span style="color:#ff1493"&gt;&amp;quot;$1&amp;quot;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;if&lt;/span&gt; &lt;span style="color:#676767"&gt;[ -&lt;/span&gt;f &lt;span style="color:#ff1493"&gt;&amp;quot;$fileName&amp;quot;&lt;/span&gt; &lt;span style="color:#676767"&gt;]&lt;/span&gt;&lt;br /&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;then&lt;/span&gt;&lt;br /&gt;    Dir&lt;span style="color:#676767"&gt;=&lt;/span&gt;&lt;span style="color:#ff1493"&gt;&amp;quot;$(dirname &amp;quot;&lt;/span&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;$fileName&lt;/span&gt;&lt;span style="color:#ff1493"&gt;&amp;quot;)&amp;quot;&lt;/span&gt;&lt;br /&gt;    Base&lt;span style="color:#676767"&gt;=&lt;/span&gt;&lt;span style="color:#ff1493"&gt;&amp;quot;$(basename &amp;quot;&lt;/span&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;$fileName&lt;/span&gt;&lt;span style="color:#ff1493"&gt;&amp;quot;)&amp;quot;&lt;/span&gt;&lt;br /&gt;    AbsDir&lt;span style="color:#676767"&gt;=&lt;/span&gt;&lt;span style="color:#ff1493"&gt;&amp;quot;$(cd &amp;quot;&lt;/span&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;$Dir&lt;/span&gt;&lt;span style="color:#ff1493"&gt;&amp;quot; &amp;amp;&amp;amp; pwd -P)&amp;quot;&lt;/span&gt;&lt;br /&gt;    AbsPath&lt;span style="color:#676767"&gt;=&lt;/span&gt;&lt;span style="color:#ff1493"&gt;&amp;quot;$AbsDir/$Base&amp;quot;&lt;/span&gt;&lt;br /&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;else&lt;/span&gt;&lt;br /&gt;    &lt;span style="color:#2e8b57; font-weight:bold"&gt;echo&lt;/span&gt; &lt;span style="color:#ff1493"&gt;&amp;quot;Unknown file: $fileName&amp;quot;&lt;/span&gt; &lt;span style="color:#676767"&gt;&amp;gt;&amp;amp;&lt;/span&gt;&lt;span style="color:#ff1493"&gt;2&lt;/span&gt;&lt;br /&gt;    &lt;span style="color:#2e8b57; font-weight:bold"&gt;exit&lt;/span&gt; &lt;span style="color:#ff1493"&gt;1&lt;/span&gt;&lt;br /&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;fi&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;echo $AbsPath&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color:#676767"&gt;/&lt;/span&gt;usr&lt;span style="color:#676767"&gt;/&lt;/span&gt;bin&lt;span style="color:#676767"&gt;/&lt;/span&gt;osascript &lt;span style="color:#676767"&gt;&amp;gt; /&lt;/span&gt;dev&lt;span style="color:#676767"&gt;/&lt;/span&gt;null &lt;span style="color:#676767"&gt;&amp;lt;&amp;lt;&lt;/span&gt;ASCPT&lt;br /&gt;    &lt;span style="color:#2e8b57; font-weight:bold"&gt;set&lt;/span&gt; fileToOpen to POSIX &lt;span style="color:#696969; font-weight:bold"&gt;file&lt;/span&gt; &lt;span style="color:#ff1493"&gt;&amp;quot;$AbsPath&amp;quot;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    &lt;span style="color:#2e8b57; font-weight:bold"&gt;if&lt;/span&gt; &lt;span style="color:#2e8b57; font-weight:bold"&gt;$lineGiven&lt;/span&gt; &lt;span style="color:#2e8b57; font-weight:bold"&gt;then&lt;/span&gt;&lt;br /&gt;        showLine of fileToOpen &lt;span style="color:#696969; font-weight:bold"&gt;at&lt;/span&gt; &lt;span style="color:#000069"&gt;${lineToShow:-0}&lt;/span&gt;&lt;br /&gt;    &lt;span style="color:#2e8b57; font-weight:bold"&gt;else&lt;/span&gt;&lt;br /&gt;        tell application &lt;span style="color:#ff1493"&gt;&amp;quot;SubEthaEdit&amp;quot;&lt;/span&gt;&lt;br /&gt;            activate&lt;br /&gt;            open fileToOpen&lt;br /&gt;        end tell&lt;br /&gt;    end &lt;span style="color:#2e8b57; font-weight:bold"&gt;if&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    to showLine of fileToOpen &lt;span style="color:#696969; font-weight:bold"&gt;at&lt;/span&gt; lineNumber&lt;br /&gt;        tell application &lt;span style="color:#ff1493"&gt;&amp;quot;SubEthaEdit&amp;quot;&lt;/span&gt;&lt;br /&gt;            activate&lt;br /&gt;            &lt;span style="color:#2e8b57; font-weight:bold"&gt;set&lt;/span&gt; activeFile to open fileToOpen&lt;br /&gt;            tell activeFile&lt;br /&gt;                &lt;span style="color:#2e8b57; font-weight:bold"&gt;set&lt;/span&gt; selection to paragraph lineNumber&lt;br /&gt;            end tell&lt;br /&gt;        end tell&lt;br /&gt;        scrollToVisible&lt;span style="color:#676767"&gt;()&lt;/span&gt;&lt;br /&gt;    end showLine&lt;br /&gt;&lt;br /&gt;    to scrollToVisible&lt;span style="color:#676767"&gt;()&lt;/span&gt;&lt;br /&gt;        tell application &lt;span style="color:#ff1493"&gt;&amp;quot;System Events&amp;quot;&lt;/span&gt;&lt;br /&gt;            &lt;span style="color:#2e8b57; font-weight:bold"&gt;if&lt;/span&gt; UI elements enabled &lt;span style="color:#2e8b57; font-weight:bold"&gt;then&lt;/span&gt;&lt;br /&gt;                tell process &lt;span style="color:#ff1493"&gt;&amp;quot;SubEthaEdit&amp;quot;&lt;/span&gt;&lt;br /&gt;                    tell menu bar &lt;span style="color:#ff1493"&gt;1&lt;/span&gt;&lt;br /&gt;                        tell menu bar item &lt;span style="color:#ff1493"&gt;&amp;quot;Find&amp;quot;&lt;/span&gt;&lt;br /&gt;                            tell menu &lt;span style="color:#ff1493"&gt;1&lt;/span&gt;&lt;br /&gt;                                click menu item &lt;span style="color:#ff1493"&gt;&amp;quot;Jump to Selection&amp;quot;&lt;/span&gt;&lt;br /&gt;                            end tell&lt;br /&gt;                        end tell&lt;br /&gt;                    end tell&lt;br /&gt;                end tell&lt;br /&gt;            end &lt;span style="color:#2e8b57; font-weight:bold"&gt;if&lt;/span&gt;&lt;br /&gt;        end tell&lt;br /&gt;    end scrollToVisible&lt;br /&gt;ASCPT&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3164056879958238384-1653914993805981122?l=appliedabstraction.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://appliedabstraction.blogspot.com/feeds/1653914993805981122/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3164056879958238384&amp;postID=1653914993805981122' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/1653914993805981122'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/1653914993805981122'/><link rel='alternate' type='text/html' href='http://appliedabstraction.blogspot.com/2009/11/replacement-for-subethaedit-command.html' title='Replacement for SubEthaEdit&amp;#39;s Command Line Tool'/><author><name>Michael Barber</name><uri>http://www.blogger.com/profile/15713879666980145844</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3164056879958238384.post-5238942184519461100</id><published>2009-08-24T12:14:00.001-07:00</published><updated>2009-08-24T12:14:05.902-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Scala'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>The Hard Part of Getting Started With Scala</title><content type='html'>In an  &lt;a href="http://www.computerworld.com.au/article/315254/-z_programming_languages_scala"&gt;interesting interview,&lt;/a&gt; Martin Odersky notes:&lt;br /&gt;&lt;blockquote&gt;Clearly it is easiest for a Java or .NET developer to learn Scala. For other communities, the stumbling blocks don't have so much to do with the language itself as with the way we package it and the way the tools are set up, which is Java specific. Once they learn how these things are set up, it should not be hard to learn the language itself.&lt;br /&gt;&lt;/blockquote&gt;&lt;br /&gt;I'd already come to this conclusion on my own, as someone who has never used Java and is trying to guess at the Java conventions needed for Scala. I think this is typical of newer languages: you can't easily learn them without knowing a specific other language already. &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3164056879958238384-5238942184519461100?l=appliedabstraction.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://appliedabstraction.blogspot.com/feeds/5238942184519461100/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3164056879958238384&amp;postID=5238942184519461100' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/5238942184519461100'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/5238942184519461100'/><link rel='alternate' type='text/html' href='http://appliedabstraction.blogspot.com/2009/08/hard-part-of-getting-started-with-scala.html' title='The Hard Part of Getting Started With Scala'/><author><name>Michael Barber</name><uri>http://www.blogger.com/profile/15713879666980145844</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3164056879958238384.post-9075451593355695869</id><published>2009-05-09T14:56:00.001-07:00</published><updated>2009-05-09T14:56:03.098-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='tips'/><category scheme='http://www.blogger.com/atom/ns#' term='humor'/><category scheme='http://www.blogger.com/atom/ns#' term='Plaxo'/><title type='text'>Hidden Setting for Plaxo</title><content type='html'>I use &lt;a href="http://www.plaxo.com"&gt;Plaxo&lt;/a&gt;, mainly for backing up my address book. Plaxo also lets you connect a number of online services to your Plaxo profile and share updates with connected acquaintances, providing a sort of &lt;em&gt;ad hoc&lt;/em&gt; online social network. &lt;br /&gt;&lt;br /&gt;And Plaxo nags you every time you go to your profile page, asking you to update your status by entering a little text into a field. There doesn't seem to be any way to turn off the status report feature, so you also don't have any way to get it to quit nagging you. But I've found the hidden setting that lets the people linked to you know that you're not using the status reports, and, more importantly, gets Plaxo to quit nagging you. &lt;br /&gt;&lt;br /&gt;All you have to do is enter the secret text into the status report field. The secret text reads:  "is not providing status reports." &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3164056879958238384-9075451593355695869?l=appliedabstraction.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://appliedabstraction.blogspot.com/feeds/9075451593355695869/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3164056879958238384&amp;postID=9075451593355695869' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/9075451593355695869'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/9075451593355695869'/><link rel='alternate' type='text/html' href='http://appliedabstraction.blogspot.com/2009/05/hidden-setting-for-plaxo.html' title='Hidden Setting for Plaxo'/><author><name>Michael Barber</name><uri>http://www.blogger.com/profile/15713879666980145844</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3164056879958238384.post-1630148878920178036</id><published>2009-05-01T08:08:00.001-07:00</published><updated>2009-05-01T08:08:04.353-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>"Hello World" for Numerics</title><content type='html'>I'm taking a serious look at &lt;a href="http://www.scala-lang.org/"&gt;Scala&lt;/a&gt;, and liking what I see very much. It struck me that I have my own version of the standard &lt;a href="http://en.wikipedia.org/wiki/Hello_world_program"&gt;Hello World&lt;/a&gt; program. I recognize, of course, that getting a program to print "Hello World" is not about testing I/O, but instead seeing that you can get a program running at all. Nonetheless, the first test I always make is a different one, that tells me something &lt;em&gt;vital&lt;/em&gt;.&lt;br /&gt;&lt;br /&gt;I do this:&lt;br /&gt;&lt;pre&gt;[~ 479] scala&lt;br /&gt;Welcome to Scala version 2.7.3.final (Java HotSpot(TM) Client VM, Java 1.5.0_16).&lt;br /&gt;Type in expressions to have them evaluated.&lt;br /&gt;Type :help for more information.&lt;br /&gt;&lt;br /&gt;scala&gt; 1 + 2*3&lt;br /&gt;res0: Int = 7&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;If &lt;code&gt;1+2*3&lt;/code&gt; returns a value other than 7, I automatically reject the language. For completeness, I also check &lt;code&gt;2*3+1&lt;/code&gt;. What programming I do is normally math-intensive enough that I refuse to deal with languages that can't handle normal precedence rules.&lt;br /&gt;&lt;br /&gt;Prefix languages like the Lisp family pass this test, as they won't return a value. I've not worked with postfix languages like Forth, but I think they'd pass, too. &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3164056879958238384-1630148878920178036?l=appliedabstraction.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://appliedabstraction.blogspot.com/feeds/1630148878920178036/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3164056879958238384&amp;postID=1630148878920178036' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/1630148878920178036'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/1630148878920178036'/><link rel='alternate' type='text/html' href='http://appliedabstraction.blogspot.com/2009/05/world-for-numerics.html' title='&amp;quot;Hello World&amp;quot; for Numerics'/><author><name>Michael Barber</name><uri>http://www.blogger.com/profile/15713879666980145844</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3164056879958238384.post-8982289722757674557</id><published>2009-04-10T08:25:00.001-07:00</published><updated>2009-04-10T08:25:19.088-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Automator'/><category scheme='http://www.blogger.com/atom/ns#' term='AppleScript'/><category scheme='http://www.blogger.com/atom/ns#' term='SubEthaEdit'/><category scheme='http://www.blogger.com/atom/ns#' term='Mac OS X'/><title type='text'>Experimenting with Implementing Automator Actions</title><content type='html'>I've recently experimented with implementing actions for Automator using AppleScript. Overall, it was an interesting experience, and one I'd definitely recommend trying yourself. &lt;br /&gt;&lt;br /&gt;Creating an action is generally quite simple, consisting of three main steps: &lt;br /&gt;&lt;ol&gt;&lt;br /&gt;&lt;li&gt;Laying out controls in Interface Builder and establishing bindings to an &lt;code&gt;NSObjectController&lt;/code&gt;, &lt;/li&gt;&lt;br /&gt;&lt;li&gt;Defining the properties of the action by filling out some values in plists,&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Writing an AppleScript to actually implement the behavior.&lt;/li&gt;&lt;br /&gt;&lt;/ol&gt;&lt;br /&gt;I found &lt;a href="http://developer.apple.com/documentation/appleapplications/conceptual/AutomatorConcepts/Articles/DevelopAction.html"&gt;Apple's documentation&lt;/a&gt; and an &lt;a href="http://www.macdevcenter.com/pub/a/mac/2005/05/03/automator.html"&gt;article by Matt Neuburg&lt;/a&gt; from the now-defunct O'Reilly Mac developer center. The latter article is a nice tutorial, but a little dated in spots; some caution is warranted.&lt;br /&gt;&lt;br /&gt;As a specific example, I decided to take some of the &lt;a href="http://appliedabstraction.blogspot.com/2008/01/seeing-latex-19-subethaedittools-or.html"&gt;AppleScript handlers for SubEthaEdit&lt;/a&gt; I already had on hand and convert them into actions. After reviewing the possibilities, I decided that there were just two actions that looked sensible: getting text from the front SubEthaEdit document and setting text in the front SubEthaEdit document. These are quite similar to existing actions for TextEdit. Additionally, I decided to expand them a little bit to also allow getting or setting the selection or selected lines in the document, not just the full contents of the document. &lt;br /&gt;&lt;br /&gt;The first step was pretty simple. With well-defined, compact actions like the ones I considered, the interface requires little thought and little effort. In my case, it was just a matter of populating the menu entries for a pop-up button and establishing a binding. &lt;br /&gt;&lt;br /&gt;To my surprise, I spent the most time and had the most difficulty with the second step. I'd expected this to be easy, but found that there were quite a few details that weren't made especially clear in the description of the keys for the property lists. The analogous actions for TextEdit proved very helpful, here: in several cases, I determined the correct choices by examining the plists from the bundles for the TextEdit actions. I'd expect this step to get easier with practice.&lt;br /&gt;&lt;br /&gt;Actually implementing the behavior was especially easy, in this case. As I already had working handlers, I was able to just focus on connecting the action to the handlers. The structure for this is quite pleasant: a record of parameters is injected into the &lt;code&gt;run&lt;/code&gt; handler, containing values obtained from the bindings to the &lt;code&gt;NSObjectController&lt;/code&gt;. The resulting structure for my actions was then just using &lt;code&gt;if-then&lt;/code&gt; statements to interpret the parameters, calling out to different handlers. &lt;br /&gt;&lt;br /&gt;Although implementation of the behavior was easy, I did find testing to be quite tedious. It was helpful to save a workflow for testing, as &lt;a href="http://www.macdevcenter.com/pub/a/mac/2005/05/03/automator.html"&gt;suggested by Neuburg&lt;/a&gt;. More importantly than that, I'd strongly recommend that you have working handlers before trying to make them part of your action—test out the handlers in, e.g., Script Editor, and test that your Automator action dispatches to the correct handler. &lt;br /&gt;&lt;br /&gt;The &lt;a href="http://www.box.net/shared/hcqjjjtll8"&gt;actions&lt;/a&gt; I created are available, as are the &lt;a href="http://www.box.net/shared/s032rz538j"&gt;sources&lt;/a&gt;. &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3164056879958238384-8982289722757674557?l=appliedabstraction.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://appliedabstraction.blogspot.com/feeds/8982289722757674557/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3164056879958238384&amp;postID=8982289722757674557' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/8982289722757674557'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/8982289722757674557'/><link rel='alternate' type='text/html' href='http://appliedabstraction.blogspot.com/2009/04/experimenting-with-implementing.html' title='Experimenting with Implementing Automator Actions'/><author><name>Michael Barber</name><uri>http://www.blogger.com/profile/15713879666980145844</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3164056879958238384.post-8731412083629655256</id><published>2009-02-18T12:00:00.001-08:00</published><updated>2009-02-18T12:04:10.470-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='web development'/><category scheme='http://www.blogger.com/atom/ns#' term='tutorial'/><category scheme='http://www.blogger.com/atom/ns#' term='Mac OS X'/><category scheme='http://www.blogger.com/atom/ns#' term='SkyBlueCanvas'/><title type='text'>Setting up SkyBlueCanvas for Local Use on Mac OS X Leopard</title><content type='html'>&lt;a href="http://www.skybluecanvas.com/"&gt;SkyBlueCanvas&lt;/a&gt; (SBC) is a content management system (CMS) for web sites. It can be set up to run on a Mac OS X system using Web Sharing, but some steps need to be taken first. The result is a subfolder of your Sites folder, which shows off pretty much the full features of SBC. Unfortunately, you won't get to this point just by naively following the installation instructions for SBC, because it depends on features not enabled by default in Mac OS X.&lt;br /&gt;&lt;br /&gt;Note that I'm not making any recommendations as to whether or not you should use SBC, but am instead just sharing what I had to do to get SBC working. This was driven by my own interest in trying out SBC. I have insufficient experience with CMSs in general or SBC in particular to make an informed judgement. &lt;br /&gt;&lt;br /&gt;Several of the steps are multi-step themselves. I've just linked to the websites I found that provided a useful description. Total time to set things up should be under half an hour. After the initial set-up, starting a new site can be done by starting with step four, taking under five minutes.&lt;br /&gt;&lt;ol&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://foundationphp.com/tutorials/php_leopard.php"&gt;Enable PHP&lt;/a&gt;.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Make sure that your &lt;a href="http://support.apple.com/kb/TA25038"&gt;Sites folder is reachable&lt;/a&gt;. &lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://blog.e-nunes.com.br/2008/09/how-to-enable-htaccess-in-mac-os-x-105.html"&gt;Enable .htaccess files&lt;/a&gt;.&lt;/li&gt;&lt;br /&gt;&lt;li&gt; Copy the SkyBlueCanvas folder to your Sites folder, and give it a reasonable name (I used &lt;cite&gt;skyblue&lt;/cite&gt;, and will assume you do the same). &lt;/li&gt;&lt;br /&gt;&lt;li&gt;Change the file permissions of the skyblue folder to 755. You can do this in the Terminal with a command like &lt;code&gt;chmod -R 755 ~&lt;cite&gt;username&lt;/cite&gt;/Site/skyblue&lt;/code&gt;—replace &lt;code&gt;&lt;cite&gt;username&lt;/cite&gt;&lt;/code&gt; with your actual user name, of course. (I didn't actually find this to be necessary, although it is in the SBC installation instructions.) &lt;/li&gt;&lt;br /&gt;&lt;li&gt; Edit htaccess.txt in the skyblue folder, adding a second line of &lt;code&gt;RewriteBase /~&lt;cite&gt;username&lt;/cite&gt;/skyblue/&lt;/code&gt;. Rename htaccess.txt to .htaccess.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Change the group and owner for the SBC files. In Terminal, change to the skyblue directory, and run &lt;code&gt;sudo chown -R www:www *&lt;/code&gt;. If you don't do this, PHP won't be able to make needed changes to the files. &lt;/li&gt;&lt;br /&gt;&lt;li&gt;Start Apache. Open up the Sharing panel in the System Preferences, and check Web Sharing.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Point your web browser at &lt;code&gt;http://localhost/~username/skyblue/&lt;/code&gt; and follow the instructions. When asked for the site URL, give &lt;code&gt;http://localhost/~username/skyblue/&lt;/code&gt;. &lt;/li&gt;&lt;br /&gt;&lt;/ol&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3164056879958238384-8731412083629655256?l=appliedabstraction.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://appliedabstraction.blogspot.com/feeds/8731412083629655256/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3164056879958238384&amp;postID=8731412083629655256' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/8731412083629655256'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/8731412083629655256'/><link rel='alternate' type='text/html' href='http://appliedabstraction.blogspot.com/2009/02/setting-up-skybluecanvas-for-local-use.html' title='Setting up SkyBlueCanvas for Local Use on Mac OS X Leopard'/><author><name>Michael Barber</name><uri>http://www.blogger.com/profile/15713879666980145844</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3164056879958238384.post-4809659438602611922</id><published>2009-01-17T06:40:00.001-08:00</published><updated>2009-01-17T06:40:42.070-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='functional programming'/><category scheme='http://www.blogger.com/atom/ns#' term='W3R'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='abstraction'/><title type='text'>W3R-7: Summary and Assessment</title><content type='html'>In the &lt;a href="http://appliedabstraction.blogspot.com/search/label/W3R"&gt;&lt;cite&gt;What's Wrong With Reduce&lt;/cite&gt;&lt;/a&gt; series, I've attempted to resolve &lt;a href="http://appliedabstraction.blogspot.com/2008/12/what-wrong-with-reduce.html"&gt;contrasting views&lt;/a&gt; of the usefulness of higher order function called &lt;code&gt;reduce&lt;/code&gt; or &lt;code&gt;fold&lt;/code&gt;. By examining the &lt;a href="http://appliedabstraction.blogspot.com/2009/01/w3r-1-origins-of-reduce.html"&gt;origins&lt;/a&gt; of &lt;code&gt;reduce&lt;/code&gt;, we saw that it is an abstraction over a particular recursive pattern of control flow.  Further, &lt;code&gt;reduce&lt;/code&gt; can be &lt;a href="http://appliedabstraction.blogspot.com/2009/01/w3r-2-importing-reduce-into-python.html"&gt;readily&lt;/a&gt; &lt;a href="http://appliedabstraction.blogspot.com/2009/01/w3r-3-object-oriented-reduce.html"&gt;defined&lt;/a&gt; in Python. &lt;br /&gt;&lt;br /&gt;However, it is easy to find &lt;a href="http://appliedabstraction.blogspot.com/2009/01/w3r-4-what-wrong-with-reduce.html"&gt;cases&lt;/a&gt; where &lt;code&gt;reduce&lt;/code&gt; works well for a functional programming language but poorly in Python. In essence,  &lt;code&gt;reduce&lt;/code&gt; is an abstraction over a common pattern for functional programming languages that is uncommon in imperative programming languages like Python. Instead, a much narrower selection of Python procedures map well onto the &lt;code&gt;reduce&lt;/code&gt; control abstraction. &lt;br /&gt;&lt;br /&gt;By &lt;a href="http://appliedabstraction.blogspot.com/2009/01/w3r-5-cart-follows-horse.html"&gt;examining&lt;/a&gt; control flow patterns actually used in Python, a more appropriate higher order procedure&lt;a href="#Note1" id="refNote1"&gt;&lt;sup&gt;1&lt;/sup&gt;&lt;/a&gt;, &lt;code&gt;app&lt;/code&gt;, was identified as an alternative to &lt;code&gt;reduce&lt;/code&gt;. Several common control flow patterns cannot be captured by &lt;code&gt;app&lt;/code&gt;, so a dual &lt;code&gt;gen&lt;/code&gt; procedure was also &lt;a href="http://appliedabstraction.blogspot.com/2009/01/w3r-6-dualing-reduce.html"&gt;defined&lt;/a&gt;. &lt;br /&gt;&lt;br /&gt;Between &lt;code&gt;app&lt;/code&gt; and &lt;code&gt;gen&lt;/code&gt;, we can define many functions to process or create iterators. However, an important question remains: should we use &lt;code&gt;app&lt;/code&gt; or &lt;code&gt;gen&lt;/code&gt;? I'd have to say that it isn't worthwhile. Both &lt;code&gt;app&lt;/code&gt; and &lt;code&gt;gen&lt;/code&gt; are abstractions over imperative patterns, depending crucially on side effects. As a consequence, it is of equal importance to understand the order in which, say, &lt;code&gt;app&lt;/code&gt; processes the elements of an iterator. Thus, &lt;code&gt;app&lt;/code&gt; fails to provide any conceptual benefit over just using a &lt;code&gt;for&lt;/code&gt; loop—in contrast, functional programming with &lt;code&gt;reduce&lt;/code&gt;, &lt;code&gt;map&lt;/code&gt;, and other higher order functions lets you ignore the order in which things are done. &lt;br /&gt;&lt;br /&gt;That said, I use and define higher order procedures all the time in Python. I just define them as (generator) functions using imperative constructs like &lt;code&gt;for&lt;/code&gt; or &lt;code&gt;while&lt;/code&gt; loops. Also, like the &lt;code&gt;itertools&lt;/code&gt; module, my higher order procedures typically take iterators as arguments or return them as results. So long as iterators only need to be used once, this looks a lot like functional programming. Really, it is closer to &lt;a href="http://en.wikipedia.org/wiki/Dataflow_programming"&gt;dataflow programming&lt;/a&gt;. &lt;br /&gt;&lt;br /&gt;Having an effective alternative to &lt;code&gt;reduce&lt;/code&gt; is relatively unimportant for this iterator-intensive programming style. Far more useful would be higher order procedures that simplify composing multiple procedures taking and returning iterators. The overall result should be more like constructing a shell pipeline instead of the extensive nesting of procedure calls currently seen. &lt;br /&gt;&lt;br /&gt;&lt;hr/&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="#refNote1" id="Note1"&gt;&lt;sup&gt;1&lt;/sup&gt;&lt;/a&gt;  For clarity, I distinguish here between higher order &lt;em&gt;functions&lt;/em&gt; and higher order &lt;em&gt;procedures&lt;/em&gt;. Functions should really be mathematical functions, i.e., no side effects, while procedures are more general. &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3164056879958238384-4809659438602611922?l=appliedabstraction.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://appliedabstraction.blogspot.com/feeds/4809659438602611922/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3164056879958238384&amp;postID=4809659438602611922' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/4809659438602611922'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/4809659438602611922'/><link rel='alternate' type='text/html' href='http://appliedabstraction.blogspot.com/2009/01/w3r-7-summary-and-assessment.html' title='W3R-7: Summary and Assessment'/><author><name>Michael Barber</name><uri>http://www.blogger.com/profile/15713879666980145844</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3164056879958238384.post-3027865056569407453</id><published>2009-01-08T03:18:00.001-08:00</published><updated>2009-01-08T03:18:02.420-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='functional programming'/><category scheme='http://www.blogger.com/atom/ns#' term='W3R'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='abstraction'/><title type='text'>W3R-6: Dualing Reduce</title><content type='html'>By examining actual programs, we saw that &lt;code&gt;reduce&lt;/code&gt; is &lt;a href="http://appliedabstraction.blogspot.com/2009/01/w3r-4-what-wrong-with-reduce.html"&gt;the wrong control abstraction&lt;/a&gt; in many cases. By starting from programs instead of starting with the abstractions, we saw that &lt;code&gt;app&lt;/code&gt; is a more natural control abstraction for Python. The comparatively few cases where &lt;code&gt;reduce&lt;/code&gt; is right were handled by &lt;a href="http://appliedabstraction.blogspot.com/2009/01/w3r-5-cart-follows-horse.html"&gt;introducing an adaptor&lt;/a&gt; &lt;code&gt;Accumulator&lt;/code&gt;, with &lt;code&gt;app&lt;/code&gt; and &lt;code&gt;Accumulator&lt;/code&gt; being sufficient to define &lt;code&gt;reduce&lt;/code&gt;.&lt;br /&gt;&lt;br /&gt;Unfortunately, the approach of defining an adaptor to get &lt;code&gt;app&lt;/code&gt; to do what we want leaves out an extremely common way that &lt;code&gt;for&lt;/code&gt; loops get used. Consider defining &lt;code&gt;map&lt;/code&gt; in terms of the lazy &lt;code&gt;imap&lt;/code&gt;&lt;a href="#Note1" id="refNote1"&gt;&lt;sup&gt;1&lt;/sup&gt;&lt;/a&gt;. It looks something like:&lt;pre style="color:#000000; background-color:#fff5ee; font-size:10pt; font-family:'Courier New';"&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;def&lt;/span&gt; &lt;span style="color:#000069"&gt;map_lazy&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;function&lt;span style="color:#000000"&gt;,&lt;/span&gt; iterable&lt;span style="color:#000000"&gt;):&lt;/span&gt;&lt;br /&gt;    &lt;span style="color:#2e8b57; font-weight:bold"&gt;return&lt;/span&gt; &lt;span style="color:#2e8b57; font-weight:bold"&gt;list&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;&lt;span style="color:#000069"&gt;imap&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;function&lt;span style="color:#000000"&gt;,&lt;/span&gt; iterable&lt;span style="color:#000000"&gt;))&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;def&lt;/span&gt; &lt;span style="color:#000069"&gt;imap&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;function&lt;span style="color:#000000"&gt;,&lt;/span&gt; iterable&lt;span style="color:#000000"&gt;):&lt;/span&gt;&lt;br /&gt;    &lt;span style="color:#2e8b57; font-weight:bold"&gt;for&lt;/span&gt; x &lt;span style="color:#2e8b57; font-weight:bold"&gt;in&lt;/span&gt; iterable&lt;span style="color:#000000"&gt;:&lt;/span&gt;&lt;br /&gt;        &lt;span style="color:#2e8b57; font-weight:bold"&gt;yield&lt;/span&gt; &lt;span style="color:#000069"&gt;function&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;x&lt;span style="color:#000000"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt; All the real work is now done in &lt;code&gt;imap&lt;/code&gt;. However, &lt;code&gt;imap&lt;/code&gt; &lt;em&gt;cannot&lt;/em&gt; be written using &lt;code&gt;app&lt;/code&gt;, since there is no way to pass in a function that will &lt;code&gt;yield&lt;/code&gt; values we can use&lt;a href="#Note2" id="refNote2"&gt;&lt;sup&gt;2&lt;/sup&gt;&lt;/a&gt;. &lt;br /&gt;&lt;br /&gt;In &lt;code&gt;imap&lt;/code&gt;, both &lt;code&gt;for&lt;/code&gt; and &lt;code&gt;yield&lt;/code&gt; express control flow, distinct from that given by &lt;code&gt;app&lt;/code&gt;. Similarly, consider defining &lt;code&gt;filter&lt;/code&gt; using an &lt;code&gt;ifilter&lt;/code&gt;:&lt;pre style="color:#000000; background-color:#fff5ee; font-size:10pt; font-family:'Courier New';"&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;def&lt;/span&gt; &lt;span style="color:#000069"&gt;ifilter&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;predicate&lt;span style="color:#000000"&gt;,&lt;/span&gt; iterable&lt;span style="color:#000000"&gt;):&lt;/span&gt;&lt;br /&gt;    &lt;span style="color:#2e8b57; font-weight:bold"&gt;for&lt;/span&gt; x &lt;span style="color:#2e8b57; font-weight:bold"&gt;in&lt;/span&gt; iterable&lt;span style="color:#000000"&gt;:&lt;/span&gt;&lt;br /&gt;        &lt;span style="color:#2e8b57; font-weight:bold"&gt;if&lt;/span&gt; &lt;span style="color:#000069"&gt;predicate&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;x&lt;span style="color:#000000"&gt;):&lt;/span&gt;&lt;br /&gt;            &lt;span style="color:#2e8b57; font-weight:bold"&gt;yield&lt;/span&gt; x&lt;br /&gt;&lt;/pre&gt; In &lt;code&gt;ifilter&lt;/code&gt;, we have another pattern of control flow, regulated with an &lt;code&gt;if&lt;/code&gt; statement. Thus, we need an additional higher order procedures if we want to abstract over these patterns. It should be, informally, a dual to &lt;code&gt;app&lt;/code&gt;, creating an iterator instead of consuming one. &lt;br /&gt;&lt;br /&gt;I don't know of a standard name for the dual procedure—it's a generator (probably confusing to call it that) and has a great deal of similarity to &lt;a href="http://en.wikipedia.org/wiki/Anamorphism"&gt;unfolds&lt;/a&gt;. At the risk of some confusion, I'll go with the short name of &lt;code&gt;gen&lt;/code&gt;. A possibility for writing it is:&lt;pre style="color:#000000; background-color:#fff5ee; font-size:10pt; font-family:'Courier New';"&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;def&lt;/span&gt; &lt;span style="color:#000069"&gt;gen&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;init&lt;span style="color:#000000"&gt;,&lt;/span&gt; update&lt;span style="color:#000000"&gt;,&lt;/span&gt; shouldEmit&lt;span style="color:#000000"&gt;,&lt;/span&gt; emit&lt;span style="color:#000000"&gt;):&lt;/span&gt;&lt;br /&gt;    state &lt;span style="color:#000000"&gt;=&lt;/span&gt; init&lt;br /&gt;    &lt;span style="color:#2e8b57; font-weight:bold"&gt;while True&lt;/span&gt;&lt;span style="color:#000000"&gt;:&lt;/span&gt;&lt;br /&gt;        &lt;span style="color:#2e8b57; font-weight:bold"&gt;if&lt;/span&gt; &lt;span style="color:#000069"&gt;shouldEmit&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;state&lt;span style="color:#000000"&gt;):&lt;/span&gt;&lt;br /&gt;            &lt;span style="color:#2e8b57; font-weight:bold"&gt;yield&lt;/span&gt; &lt;span style="color:#000069"&gt;emit&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;state&lt;span style="color:#000000"&gt;)&lt;/span&gt;&lt;br /&gt;        &lt;span style="color:#000069"&gt;update&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;state&lt;span style="color:#000000"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt; Note that I've included no explicit treatment of the internal state, so &lt;code&gt;gen&lt;/code&gt; can in principle handle any sort of internal state, be it an iterator or otherwise.&lt;br /&gt;&lt;br /&gt;I don't think I've seen anything like &lt;code&gt;gen&lt;/code&gt; in Python code. Instead, generator functions are written out explicitly on a case-by-case basis. Looking at how we'd implement &lt;code&gt;imap&lt;/code&gt; with &lt;code&gt;gen&lt;/code&gt; suggests why:&lt;pre style="color:#000000; background-color:#fff5ee; font-size:10pt; font-family:'Courier New';"&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;def&lt;/span&gt; &lt;span style="color:#000069"&gt;imap_gen&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;function&lt;span style="color:#000000"&gt;,&lt;/span&gt; iterable&lt;span style="color:#000000"&gt;):&lt;/span&gt;&lt;br /&gt;    &lt;span style="color:#2e8b57; font-weight:bold"&gt;def&lt;/span&gt; &lt;span style="color:#000069"&gt;update&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;state&lt;span style="color:#000000"&gt;):&lt;/span&gt;&lt;br /&gt;        it &lt;span style="color:#000000"&gt;=&lt;/span&gt; state&lt;span style="color:#000000"&gt;[&lt;/span&gt;&lt;span style="color:#ff1493"&gt;2&lt;/span&gt;&lt;span style="color:#000000"&gt;]&lt;/span&gt;&lt;br /&gt;        state&lt;span style="color:#000000"&gt;[&lt;/span&gt;&lt;span style="color:#ff1493"&gt;0&lt;/span&gt;&lt;span style="color:#000000"&gt;] =&lt;/span&gt; it&lt;span style="color:#000000"&gt;.&lt;/span&gt;&lt;span style="color:#000069"&gt;next&lt;/span&gt;&lt;span style="color:#000000"&gt;()&lt;/span&gt;&lt;br /&gt;        state&lt;span style="color:#000000"&gt;[&lt;/span&gt;&lt;span style="color:#ff1493"&gt;1&lt;/span&gt;&lt;span style="color:#000000"&gt;] =&lt;/span&gt; &lt;span style="color:#2e8b57; font-weight:bold"&gt;True&lt;/span&gt;&lt;br /&gt;    &lt;span style="color:#2e8b57; font-weight:bold"&gt;def&lt;/span&gt; &lt;span style="color:#000069"&gt;emit&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;state&lt;span style="color:#000000"&gt;):&lt;/span&gt; &lt;span style="color:#2e8b57; font-weight:bold"&gt;return&lt;/span&gt; &lt;span style="color:#000069"&gt;function&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;state&lt;span style="color:#000000"&gt;[&lt;/span&gt;&lt;span style="color:#ff1493"&gt;0&lt;/span&gt;&lt;span style="color:#000000"&gt;])&lt;/span&gt;&lt;br /&gt;    &lt;span style="color:#2e8b57; font-weight:bold"&gt;def&lt;/span&gt; &lt;span style="color:#000069"&gt;shouldEmit&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;state&lt;span style="color:#000000"&gt;):&lt;/span&gt; &lt;span style="color:#2e8b57; font-weight:bold"&gt;return&lt;/span&gt; state&lt;span style="color:#000000"&gt;[&lt;/span&gt;&lt;span style="color:#ff1493"&gt;1&lt;/span&gt;&lt;span style="color:#000000"&gt;]&lt;/span&gt;&lt;br /&gt;    init &lt;span style="color:#000000"&gt;= [&lt;/span&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;None&lt;/span&gt;&lt;span style="color:#000000"&gt;,&lt;/span&gt; &lt;span style="color:#2e8b57; font-weight:bold"&gt;False&lt;/span&gt;&lt;span style="color:#000000"&gt;,&lt;/span&gt; &lt;span style="color:#2e8b57; font-weight:bold"&gt;iter&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;iterable&lt;span style="color:#000000"&gt;)]&lt;/span&gt;&lt;br /&gt;    &lt;span style="color:#2e8b57; font-weight:bold"&gt;return&lt;/span&gt; &lt;span style="color:#000069"&gt;gen&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;init&lt;span style="color:#000000"&gt;,&lt;/span&gt; update&lt;span style="color:#000000"&gt;,&lt;/span&gt; shouldEmit&lt;span style="color:#000000"&gt;,&lt;/span&gt; emit&lt;span style="color:#000000"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt; There is quite a lot of effort needed to convert &lt;code&gt;iterable&lt;/code&gt; into something that we can manage with &lt;code&gt;gen&lt;/code&gt;. A relatively simple class could be given to initialize and update the iterator state, paralleling the approach of &lt;code&gt;app&lt;/code&gt; plus an adaptor.  &lt;br /&gt;&lt;br /&gt;Perhaps there is an alternative to &lt;code&gt;gen&lt;/code&gt; that would be more natural for iterators as the internal state, while also cleanly handling non-iterable states. However, I'm not going to pursue the matter further, as &lt;code&gt;gen&lt;/code&gt; demonstrates how to encode another pattern of control complementary to that encoded by &lt;code&gt;app&lt;/code&gt; (or &lt;code&gt;reduce&lt;/code&gt;). Taken together, &lt;code&gt;app&lt;/code&gt; and &lt;code&gt;gen&lt;/code&gt; can accomplish a great deal, creating an iterator with &lt;code&gt;gen&lt;/code&gt; and processing its outputs using &lt;code&gt;app&lt;/code&gt;.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;hr/&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="#refNote1" id="Note1"&gt;&lt;sup&gt;1&lt;/sup&gt;&lt;/a&gt;  Arguably, we don't need &lt;code&gt;map&lt;/code&gt; at all when we have &lt;code&gt;imap&lt;/code&gt;. This is the case in Python 3, where &lt;code&gt;map&lt;/code&gt; returns an iterator.&lt;br /&gt;&lt;br /&gt;&lt;a href="#refNote2" id="Note2"&gt;&lt;sup&gt;2&lt;/sup&gt;&lt;/a&gt; This is a limitation of Python's coroutines. More general (and more complicated) routines, such as &lt;a href="http://lua-users.org/wiki/LuaCoroutinesVersusPythonGenerators"&gt;those in Lua&lt;/a&gt;, do allow such an implementation.&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3164056879958238384-3027865056569407453?l=appliedabstraction.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://appliedabstraction.blogspot.com/feeds/3027865056569407453/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3164056879958238384&amp;postID=3027865056569407453' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/3027865056569407453'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/3027865056569407453'/><link rel='alternate' type='text/html' href='http://appliedabstraction.blogspot.com/2009/01/w3r-6-dualing-reduce.html' title='W3R-6: Dualing Reduce'/><author><name>Michael Barber</name><uri>http://www.blogger.com/profile/15713879666980145844</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3164056879958238384.post-5551456571417010553</id><published>2009-01-06T03:36:00.001-08:00</published><updated>2009-01-06T03:36:39.894-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='functional programming'/><category scheme='http://www.blogger.com/atom/ns#' term='W3R'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='abstraction'/><title type='text'>W3R-5: Cart Follows Horse</title><content type='html'>Higher order functions can be used to implement control abstractions, but that doesn't mean all higher order functions provide useful control abstractions. &lt;a href="http://appliedabstraction.blogspot.com/2009/01/w3r-4-what-wrong-with-reduce.html"&gt;As we've seen&lt;/a&gt;, a useful control abstraction in one language may not work so well in another. In SML, folds are frequently used, while the analogous &lt;code&gt;reduce&lt;/code&gt; is uncommon in Python. In retrospect, this doesn't seem so surprising: SML is principally a functional programming language depending on declarative (i.e., recursive) control flow, while Python is a hybrid procedural and object-oriented programming depending on imperative control flow.  Where the two languages overlap is where &lt;code&gt;reduce&lt;/code&gt; becomes useful, but that overlap is relatively small.&lt;br /&gt;&lt;br /&gt;If &lt;code&gt;reduce&lt;/code&gt; abstracts over the wrong pattern of control flow, then what are the correct patterns for Python? Let's return to &lt;code&gt;map&lt;/code&gt; and see what we can conclude. An imperative definition of &lt;code&gt;map&lt;/code&gt; is natural, along the lines of:&lt;pre style="color:#000000; background-color:#fff5ee; font-size:10pt; font-family:'Courier New';"&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;def&lt;/span&gt; &lt;span style="color:#000069"&gt;map_imper&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;function&lt;span style="color:#000000"&gt;,&lt;/span&gt; iterable&lt;span style="color:#000000"&gt;):&lt;/span&gt;&lt;br /&gt;    acc &lt;span style="color:#000000"&gt;= []&lt;/span&gt;&lt;br /&gt;    &lt;span style="color:#2e8b57; font-weight:bold"&gt;for&lt;/span&gt; x &lt;span style="color:#2e8b57; font-weight:bold"&gt;in&lt;/span&gt; iterable&lt;span style="color:#000000"&gt;:&lt;/span&gt;&lt;br /&gt;        acc&lt;span style="color:#000000"&gt;.&lt;/span&gt;&lt;span style="color:#000069"&gt;append&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;&lt;span style="color:#000069"&gt;function&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;x&lt;span style="color:#000000"&gt;))&lt;/span&gt;&lt;br /&gt;    &lt;span style="color:#2e8b57; font-weight:bold"&gt;return&lt;/span&gt; acc&lt;br /&gt;&lt;/pre&gt;This version of &lt;code&gt;map&lt;/code&gt; utilizes a typical imperative pattern of control, consisting of mutating an object based on values encountered as we iterate though a collection using a &lt;code&gt;for&lt;/code&gt; loop. Python has no higher order procedure capturing this imperative pattern; there is such a higher order procedure for Standard ML, where it is called &lt;code&gt;app&lt;/code&gt;. &lt;br /&gt;&lt;br /&gt;Let's define &lt;code&gt;app&lt;/code&gt; for Python. It's easy:&lt;pre style="color:#000000; background-color:#fff5ee; font-size:10pt; font-family:'Courier New';"&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;def&lt;/span&gt; &lt;span style="color:#000069"&gt;app&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;mutate&lt;span style="color:#000000"&gt;,&lt;/span&gt; iterable&lt;span style="color:#000000"&gt;):&lt;/span&gt;&lt;br /&gt;    &lt;span style="color:#2e8b57; font-weight:bold"&gt;for&lt;/span&gt; x &lt;span style="color:#2e8b57; font-weight:bold"&gt;in&lt;/span&gt; iterable&lt;span style="color:#000000"&gt;:&lt;/span&gt;&lt;br /&gt;        &lt;span style="color:#000069"&gt;mutate&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;x&lt;span style="color:#000000"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt; Note that &lt;code&gt;app&lt;/code&gt; has no useful return value, always returning &lt;code&gt;None&lt;/code&gt;, emphasizing that it is always called for its side effects. Using &lt;code&gt;app&lt;/code&gt; to define &lt;code&gt;map&lt;/code&gt;, we obtain &lt;pre style="color:#000000; background-color:#fff5ee; font-size:10pt; font-family:'Courier New';"&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;def&lt;/span&gt; &lt;span style="color:#000069"&gt;map_app&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;function&lt;span style="color:#000000"&gt;,&lt;/span&gt; iterable&lt;span style="color:#000000"&gt;):&lt;/span&gt;&lt;br /&gt;    acc &lt;span style="color:#000000"&gt;= []&lt;/span&gt;&lt;br /&gt;    &lt;span style="color:#2e8b57; font-weight:bold"&gt;def&lt;/span&gt; &lt;span style="color:#000069"&gt;mutate&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;x&lt;span style="color:#000000"&gt;):&lt;/span&gt; acc&lt;span style="color:#000000"&gt;.&lt;/span&gt;&lt;span style="color:#000069"&gt;append&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;&lt;span style="color:#000069"&gt;function&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;x&lt;span style="color:#000000"&gt;))&lt;/span&gt;&lt;br /&gt;    &lt;span style="color:#000069"&gt;app&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;mutate&lt;span style="color:#000000"&gt;,&lt;/span&gt; iterable&lt;span style="color:#000000"&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span style="color:#2e8b57; font-weight:bold"&gt;return&lt;/span&gt; acc&lt;br /&gt;&lt;/pre&gt; The definition of &lt;code&gt;map_app&lt;/code&gt; is no simpler than that of &lt;code&gt;map_imper&lt;/code&gt;, but does illustrate an appropriate control abstraction. &lt;br /&gt;&lt;br /&gt;Although not needed for &lt;code&gt;map&lt;/code&gt;, several generalizations of &lt;code&gt;app&lt;/code&gt; could be made. One possibility would be to include optional arguments in order to allow slicing of the iterable:&lt;pre style="color:#000000; background-color:#fff5ee; font-size:10pt; font-family:'Courier New';"&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;import&lt;/span&gt; itertools&lt;br /&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;def&lt;/span&gt; &lt;span style="color:#000069"&gt;app&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;mutate&lt;span style="color:#000000"&gt;,&lt;/span&gt; iterable&lt;span style="color:#000000"&gt;,&lt;/span&gt; start&lt;span style="color:#000000"&gt;=&lt;/span&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;None&lt;/span&gt;&lt;span style="color:#000000"&gt;,&lt;/span&gt; stop&lt;span style="color:#000000"&gt;=&lt;/span&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;None&lt;/span&gt;&lt;span style="color:#000000"&gt;,&lt;/span&gt; step&lt;span style="color:#000000"&gt;=&lt;/span&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;None&lt;/span&gt;&lt;span style="color:#000000"&gt;):&lt;/span&gt;&lt;br /&gt;    &lt;span style="color:#2e8b57; font-weight:bold"&gt;if&lt;/span&gt; start &lt;span style="color:#2e8b57; font-weight:bold"&gt;is not None or&lt;/span&gt; stop &lt;span style="color:#2e8b57; font-weight:bold"&gt;is not None or&lt;/span&gt; step &lt;span style="color:#2e8b57; font-weight:bold"&gt;is not None&lt;/span&gt;&lt;span style="color:#000000"&gt;:&lt;/span&gt;&lt;br /&gt;        iterable &lt;span style="color:#000000"&gt;=&lt;/span&gt; itertools&lt;span style="color:#000000"&gt;.&lt;/span&gt;&lt;span style="color:#000069"&gt;islice&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;iterable&lt;span style="color:#000000"&gt;,&lt;/span&gt; start&lt;span style="color:#000000"&gt;,&lt;/span&gt; stop&lt;span style="color:#000000"&gt;,&lt;/span&gt; step&lt;span style="color:#000000"&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span style="color:#2e8b57; font-weight:bold"&gt;for&lt;/span&gt; x &lt;span style="color:#2e8b57; font-weight:bold"&gt;in&lt;/span&gt; iterable&lt;span style="color:#000000"&gt;:&lt;/span&gt;&lt;br /&gt;        &lt;span style="color:#000069"&gt;mutate&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;x&lt;span style="color:#000000"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt; Many other variations are possible. I'll not suggest which is correct, as that is a question best addressed by empirical analysis of existing code bases. &lt;br /&gt;&lt;br /&gt;Now let's reconsider our other motivating example, &lt;code&gt;sum&lt;/code&gt;. An imperative implementation is quite similar to that for &lt;code&gt;map&lt;/code&gt;:&lt;pre style="color:#000000; background-color:#fff5ee; font-size:10pt; font-family:'Courier New';"&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;def&lt;/span&gt; &lt;span style="color:#000069"&gt;sum_imper&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;numbers&lt;span style="color:#000000"&gt;):&lt;/span&gt;&lt;br /&gt;    acc &lt;span style="color:#000000"&gt;=&lt;/span&gt; &lt;span style="color:#ff1493"&gt;0&lt;/span&gt;&lt;br /&gt;    &lt;span style="color:#2e8b57; font-weight:bold"&gt;for&lt;/span&gt; x &lt;span style="color:#2e8b57; font-weight:bold"&gt;in&lt;/span&gt; numbers&lt;span style="color:#000000"&gt;:&lt;/span&gt;&lt;br /&gt;        acc &lt;span style="color:#000000"&gt;+=&lt;/span&gt; x&lt;br /&gt;    &lt;span style="color:#2e8b57; font-weight:bold"&gt;return&lt;/span&gt; x&lt;br /&gt;&lt;/pre&gt; This is the same pattern of control as used in &lt;code&gt;map&lt;/code&gt;, mutation within a &lt;code&gt;for&lt;/code&gt; loop, but it &lt;em&gt;cannot&lt;/em&gt; be expressed using &lt;code&gt;app&lt;/code&gt;. In &lt;code&gt;sum&lt;/code&gt;, the mutation is that of the variable &lt;code&gt;acc&lt;/code&gt;. We can't pass in a variable to &lt;code&gt;app&lt;/code&gt; and expect it to be modified, as only the local variable would be changed. That is, &lt;code&gt;app&lt;/code&gt; mutates values, while  &lt;code&gt;sum&lt;/code&gt; mutates a variable. &lt;br /&gt;&lt;br /&gt;I can think of two solutions. First, we could introduce another higher order procedure to abstract over what happens in &lt;code&gt;sum&lt;/code&gt;. That procedure—function, in this case—is just &lt;code&gt;reduce&lt;/code&gt;.  Second, we could adapt the value from an immutable number to some mutable object. Which to choose is pretty much a matter of taste. Having two substantially similar control flow mechanisms may be undesirable, but &lt;code&gt;reduce&lt;/code&gt; is also a known quantity. The adaptor approach is not as well explored, but may turn out to be more natural. Let's take a look.&lt;br /&gt;&lt;br /&gt;When combined with &lt;code&gt;app&lt;/code&gt;, the adaptor needs to produce the same effect as &lt;code&gt;reduce&lt;/code&gt;. Since &lt;code&gt;app&lt;/code&gt; is handling the control flow, the adaptor just needs to handle the accumulation. One possible definition is to use an &lt;code&gt;Accumulator&lt;/code&gt; class, defined as:&lt;pre style="color:#000000; background-color:#fff5ee; font-size:10pt; font-family:'Courier New';"&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;class&lt;/span&gt; &lt;span style="color:#000069"&gt;Accumulator&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;object&lt;/span&gt;&lt;span style="color:#000000"&gt;):&lt;/span&gt;&lt;br /&gt;    &lt;span style="color:#2e8b57; font-weight:bold"&gt;def&lt;/span&gt; &lt;span style="color:#000069"&gt;__init__&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;self&lt;span style="color:#000000"&gt;,&lt;/span&gt; function&lt;span style="color:#000000"&gt;,&lt;/span&gt; init&lt;span style="color:#000000"&gt;):&lt;/span&gt;&lt;br /&gt;        self&lt;span style="color:#000000"&gt;.&lt;/span&gt;function &lt;span style="color:#000000"&gt;=&lt;/span&gt; function&lt;br /&gt;        self&lt;span style="color:#000000"&gt;.&lt;/span&gt;value &lt;span style="color:#000000"&gt;=&lt;/span&gt; init&lt;br /&gt;&lt;br /&gt;    &lt;span style="color:#2e8b57; font-weight:bold"&gt;def&lt;/span&gt; &lt;span style="color:#000069"&gt;update&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;self&lt;span style="color:#000000"&gt;,&lt;/span&gt; x&lt;span style="color:#000000"&gt;):&lt;/span&gt;&lt;br /&gt;        self&lt;span style="color:#000000"&gt;.&lt;/span&gt;value &lt;span style="color:#000000"&gt;=&lt;/span&gt; self&lt;span style="color:#000000"&gt;.&lt;/span&gt;&lt;span style="color:#000069"&gt;function&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;self&lt;span style="color:#000000"&gt;.&lt;/span&gt;value&lt;span style="color:#000000"&gt;,&lt;/span&gt; x&lt;span style="color:#000000"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt; As an example of usage, here is an accumulator for summation:&lt;pre style="color:#000000; background-color:#fff5ee; font-size:10pt; font-family:'Courier New';"&gt;&lt;span style="color:#000000"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; acc_sum &lt;span style="color:#000000"&gt;=&lt;/span&gt; &lt;span style="color:#000069"&gt;Accumulator&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;operator&lt;span style="color:#000000"&gt;.&lt;/span&gt;add&lt;span style="color:#000000"&gt;,&lt;/span&gt; &lt;span style="color:#ff1493"&gt;0&lt;/span&gt;&lt;span style="color:#000000"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;span style="color:#000000"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; acc_sum&lt;span style="color:#000000"&gt;.&lt;/span&gt;&lt;span style="color:#000069"&gt;update&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;&lt;span style="color:#ff1493"&gt;4&lt;/span&gt;&lt;span style="color:#000000"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;span style="color:#000000"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; acc_sum&lt;span style="color:#000000"&gt;.&lt;/span&gt;value&lt;br /&gt;&lt;span style="color:#ff1493"&gt;4&lt;/span&gt;&lt;br /&gt;&lt;span style="color:#000000"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; acc_sum&lt;span style="color:#000000"&gt;.&lt;/span&gt;&lt;span style="color:#000069"&gt;update&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;&lt;span style="color:#ff1493"&gt;37&lt;/span&gt;&lt;span style="color:#000000"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;span style="color:#000000"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; acc_sum&lt;span style="color:#000000"&gt;.&lt;/span&gt;value&lt;br /&gt;&lt;span style="color:#ff1493"&gt;41&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt; Putting together an &lt;code&gt;Accumulator&lt;/code&gt; instance and &lt;code&gt;app&lt;/code&gt;, we can give another definition of &lt;code&gt;sum&lt;/code&gt;:&lt;pre style="color:#000000; background-color:#fff5ee; font-size:10pt; font-family:'Courier New';"&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;def&lt;/span&gt; &lt;span style="color:#000069"&gt;sum_adapt&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;numbers&lt;span style="color:#000000"&gt;):&lt;/span&gt;&lt;br /&gt;    acc &lt;span style="color:#000000"&gt;=&lt;/span&gt; &lt;span style="color:#000069"&gt;Accumulator&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;operator&lt;span style="color:#000000"&gt;.&lt;/span&gt;add&lt;span style="color:#000000"&gt;,&lt;/span&gt; &lt;span style="color:#ff1493"&gt;0&lt;/span&gt;&lt;span style="color:#000000"&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span style="color:#000069"&gt;app&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;acc&lt;span style="color:#000000"&gt;.&lt;/span&gt;update&lt;span style="color:#000000"&gt;,&lt;/span&gt; numbers&lt;span style="color:#000000"&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span style="color:#2e8b57; font-weight:bold"&gt;return&lt;/span&gt; acc&lt;span style="color:#000000"&gt;.&lt;/span&gt;value&lt;br /&gt;&lt;/pre&gt; As a second example, here is &lt;a href="http://appliedabstraction.blogspot.com/2009/01/w3r-2-importing-reduce-into-python.html"&gt;yet another definition&lt;/a&gt; of a   fold:&lt;pre style="color:#000000; background-color:#fff5ee; font-size:10pt; font-family:'Courier New';"&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;def&lt;/span&gt; &lt;span style="color:#000069"&gt;fold_app&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;function&lt;span style="color:#000000"&gt;,&lt;/span&gt; init&lt;span style="color:#000000"&gt;,&lt;/span&gt; iterable&lt;span style="color:#000000"&gt;):&lt;/span&gt;&lt;br /&gt;    acc &lt;span style="color:#000000"&gt;=&lt;/span&gt; &lt;span style="color:#000069"&gt;Accumulator&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;function&lt;span style="color:#000000"&gt;,&lt;/span&gt; init&lt;span style="color:#000000"&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span style="color:#000069"&gt;app&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;acc&lt;span style="color:#000000"&gt;.&lt;/span&gt;update&lt;span style="color:#000000"&gt;,&lt;/span&gt; iterable&lt;span style="color:#000000"&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span style="color:#2e8b57; font-weight:bold"&gt;return&lt;/span&gt; acc&lt;span style="color:#000000"&gt;.&lt;/span&gt;value&lt;br /&gt;&lt;/pre&gt; Personally, I find these definitions to be quite natural and appealing; &lt;code&gt;app&lt;/code&gt; and &lt;code&gt;Accumulator&lt;/code&gt; would fit in well with my programming style. &lt;br /&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3164056879958238384-5551456571417010553?l=appliedabstraction.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://appliedabstraction.blogspot.com/feeds/5551456571417010553/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3164056879958238384&amp;postID=5551456571417010553' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/5551456571417010553'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/5551456571417010553'/><link rel='alternate' type='text/html' href='http://appliedabstraction.blogspot.com/2009/01/w3r-5-cart-follows-horse.html' title='W3R-5: Cart Follows Horse'/><author><name>Michael Barber</name><uri>http://www.blogger.com/profile/15713879666980145844</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3164056879958238384.post-1397513655919769886</id><published>2009-01-04T09:11:00.001-08:00</published><updated>2009-01-05T09:09:39.246-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='functional programming'/><category scheme='http://www.blogger.com/atom/ns#' term='W3R'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>W3R-4: What's Wrong With Reduce.</title><content type='html'>We've &lt;a href="http://appliedabstraction.blogspot.com/2009/01/w3r-2-importing-reduce-into-python.html"&gt;used&lt;/a&gt; &lt;code&gt;fold&lt;/code&gt;—our model of &lt;code&gt;reduce&lt;/code&gt;—to define &lt;code&gt;sum&lt;/code&gt;, &lt;code&gt;product&lt;/code&gt;, and a function &lt;code&gt;min3&lt;/code&gt; that finds the three smallest elements in a list. We have not yet used &lt;code&gt;fold&lt;/code&gt; to define &lt;code&gt;map&lt;/code&gt;, which was one of the functions we &lt;a href="http://appliedabstraction.blogspot.com/2009/01/w3r-1-origins-of-reduce.html"&gt;used&lt;/a&gt; to motivate &lt;code&gt;fold&lt;/code&gt; in the first place. Let's do that now&lt;a href="#Note1" id="refNote1"&gt;&lt;sup&gt;1&lt;/sup&gt;&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;With &lt;a href="http://appliedabstraction.blogspot.com/2009/01/w3r-1-origins-of-reduce.html"&gt;linked lists&lt;/a&gt;, it is easy:&lt;br /&gt;&lt;pre style="color:#000000; background-color:#fff5ee; font-size:10pt; font-family:'Courier New';"&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;def&lt;/span&gt; &lt;span style="color:#000069"&gt;reverse&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;lnkLst&lt;span style="color:#000000"&gt;):&lt;/span&gt;&lt;br /&gt;    &lt;span style="color:#2e8b57; font-weight:bold"&gt;def&lt;/span&gt; &lt;span style="color:#000069"&gt;app&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;acc&lt;span style="color:#000000"&gt;,&lt;/span&gt; x&lt;span style="color:#000000"&gt;):&lt;/span&gt; &lt;span style="color:#2e8b57; font-weight:bold"&gt;return&lt;/span&gt; &lt;span style="color:#000069"&gt;Cons&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;x&lt;span style="color:#000000"&gt;,&lt;/span&gt; acc&lt;span style="color:#000000"&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span style="color:#2e8b57; font-weight:bold"&gt;return&lt;/span&gt; &lt;span style="color:#000069"&gt;fold&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;app&lt;span style="color:#000000"&gt;,&lt;/span&gt; &lt;span style="color:#000069"&gt;Nil&lt;/span&gt;&lt;span style="color:#000000"&gt;(),&lt;/span&gt; lnkLst&lt;span style="color:#000000"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;def&lt;/span&gt; &lt;span style="color:#000069"&gt;map_ll&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;function&lt;span style="color:#000000"&gt;,&lt;/span&gt; lnkLst&lt;span style="color:#000000"&gt;):&lt;/span&gt;&lt;br /&gt;    &lt;span style="color:#2e8b57; font-weight:bold"&gt;def&lt;/span&gt; &lt;span style="color:#000069"&gt;app&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;acc&lt;span style="color:#000000"&gt;,&lt;/span&gt; x&lt;span style="color:#000000"&gt;):&lt;/span&gt;&lt;br /&gt;        &lt;span style="color:#2e8b57; font-weight:bold"&gt;return&lt;/span&gt; &lt;span style="color:#000069"&gt;Cons&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;&lt;span style="color:#000069"&gt;function&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;x&lt;span style="color:#000000"&gt;),&lt;/span&gt; acc&lt;span style="color:#000000"&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span style="color:#2e8b57; font-weight:bold"&gt;return&lt;/span&gt; &lt;span style="color:#000069"&gt;reverse&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;&lt;span style="color:#000069"&gt;fold&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;app&lt;span style="color:#000000"&gt;,&lt;/span&gt; &lt;span style="color:#000069"&gt;Nil&lt;/span&gt;&lt;span style="color:#000000"&gt;(),&lt;/span&gt; lnkLst&lt;span style="color:#000000"&gt;))&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt; I've introduced a &lt;code&gt;reverse&lt;/code&gt; function to preserve the order, as we didn't bother to define a right-to-left &lt;code&gt;foldr&lt;/code&gt;, just the left-to-right &lt;code&gt;fold&lt;/code&gt; that parallels Pythons &lt;code&gt;reduce&lt;/code&gt;. Otherwise, the definition is straightforward. The accumulating function needs to create a new linked list by transforming values from the source list. This is a perfect match for &lt;code&gt;fold&lt;/code&gt;.&lt;br /&gt;&lt;br /&gt;The same idea can be applied to Python's lists (actually arrays):&lt;pre style="color:#000000; background-color:#fff5ee; font-size:10pt; font-family:'Courier New';"&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;def&lt;/span&gt; &lt;span style="color:#2e8b57; font-weight:bold"&gt;map&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;function&lt;span style="color:#000000"&gt;,&lt;/span&gt; lst&lt;span style="color:#000000"&gt;):&lt;/span&gt;&lt;br /&gt;    &lt;span style="color:#2e8b57; font-weight:bold"&gt;def&lt;/span&gt; &lt;span style="color:#000069"&gt;app&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;acc&lt;span style="color:#000000"&gt;,&lt;/span&gt; x&lt;span style="color:#000000"&gt;):&lt;/span&gt;&lt;br /&gt;        &lt;span style="color:#2e8b57; font-weight:bold"&gt;return&lt;/span&gt; acc &lt;span style="color:#000000"&gt;+ [&lt;/span&gt;&lt;span style="color:#000069"&gt;function&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;x&lt;span style="color:#000000"&gt;)]&lt;/span&gt;&lt;br /&gt;    &lt;span style="color:#2e8b57; font-weight:bold"&gt;return&lt;/span&gt; &lt;span style="color:#000069"&gt;fold&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;app&lt;span style="color:#000000"&gt;, [],&lt;/span&gt; lst&lt;span style="color:#000000"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt; However, this is quite poor. This implementation of &lt;code&gt;map&lt;/code&gt; creates a new list at each step, and thus performs very badly—&lt;code&gt;O(N&lt;sup&gt;2&lt;/sup&gt;)&lt;/code&gt; time, where &lt;code&gt;N&lt;/code&gt; is the length of the list, instead of the &lt;code&gt;O(N)&lt;/code&gt; that should be taken. Even though &lt;code&gt;fold&lt;/code&gt;—i.e., &lt;code&gt;reduce&lt;/code&gt;—is applicable to any iterable, that does not mean it has captured a useful pattern for every iterable!&lt;br /&gt;&lt;br /&gt;So what went wrong? Folds are a good way to break down data structures, which led us to iterators, the standard way to take apart data structures in Python. That worked fine, and we really have no reason to expect it to suddenly fail. However, we have introduced something new: we're also using reduce to build up a new data structure—specifically, a new list. This works fine with the functional link list but fails badly with the imperative arrays. &lt;br /&gt;&lt;br /&gt;To see this, let's expand the fold out again, using &lt;code&gt;fold_iter&lt;/code&gt; (&lt;a href="http://appliedabstraction.blogspot.com/2009/01/w3r-2-importing-reduce-into-python.html"&gt;introduced earlier&lt;/a&gt;) to do it:&lt;pre style="color:#000000; background-color:#fff5ee; font-size:10pt; font-family:'Courier New';"&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;def&lt;/span&gt; &lt;span style="color:#000069"&gt;map_iter&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;function&lt;span style="color:#000000"&gt;,&lt;/span&gt; lst&lt;span style="color:#000000"&gt;):&lt;/span&gt;&lt;br /&gt;    acc &lt;span style="color:#000000"&gt;= []&lt;/span&gt;&lt;br /&gt;    &lt;span style="color:#2e8b57; font-weight:bold"&gt;for&lt;/span&gt; x &lt;span style="color:#2e8b57; font-weight:bold"&gt;in&lt;/span&gt; iterable&lt;span style="color:#000000"&gt;:&lt;/span&gt;&lt;br /&gt;        acc &lt;span style="color:#000000"&gt;=&lt;/span&gt; acc &lt;span style="color:#000000"&gt;+ [&lt;/span&gt;&lt;span style="color:#000069"&gt;function&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;x&lt;span style="color:#000000"&gt;)]&lt;/span&gt;&lt;br /&gt;    &lt;span style="color:#2e8b57; font-weight:bold"&gt;return&lt;/span&gt; acc&lt;br /&gt;&lt;/pre&gt; The final form is just not how it should be done. In other words, &lt;code&gt;fold&lt;/code&gt; is an abstraction over the wrong pattern!&lt;br /&gt;&lt;br /&gt;What has happened is that we've gone about things entirely backwards. In a functional programming language like SML, folds are useful. That usefulness was (&lt;a href="http://appliedabstraction.blogspot.com/2009/01/w3r-1-origins-of-reduce.html"&gt;apparently&lt;/a&gt;) the basis for thinking &lt;code&gt;reduce&lt;/code&gt; should be useful in Python. However, folds are useful in functional programming languages because they capture a particular pattern of recursion that is common in functional programming. That pattern of recursion is &lt;em&gt;not&lt;/em&gt; common in Python, so &lt;em&gt;we have no reason to conclude that &lt;code&gt;reduce&lt;/code&gt; will be useful&lt;/em&gt;&lt;a href="#Note2" id="refNote2"&gt;&lt;sup&gt;2&lt;/sup&gt;&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;hr/&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="#refNote1" id="Note1"&gt;&lt;sup&gt;1&lt;/sup&gt;&lt;/a&gt; For simplicity, I'll only allow a single list to be given as an argument to &lt;code&gt;map&lt;/code&gt;, unlike the Python built-in function. &lt;br /&gt;&lt;br /&gt;&lt;a href="#refNote2" id="Note2"&gt;&lt;sup&gt;2&lt;/sup&gt;&lt;/a&gt;Nor may we conclude on logical grounds that &lt;code&gt;reduce&lt;/code&gt; will not be useful. Experience suggests it is not particularly so, but we don't know if that is something fundamental about &lt;code&gt;reduce&lt;/code&gt; or something incidental, such as the documentation. &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3164056879958238384-1397513655919769886?l=appliedabstraction.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://appliedabstraction.blogspot.com/feeds/1397513655919769886/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3164056879958238384&amp;postID=1397513655919769886' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/1397513655919769886'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/1397513655919769886'/><link rel='alternate' type='text/html' href='http://appliedabstraction.blogspot.com/2009/01/w3r-4-what-wrong-with-reduce.html' title='W3R-4: What&amp;#39;s Wrong With Reduce.'/><author><name>Michael Barber</name><uri>http://www.blogger.com/profile/15713879666980145844</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3164056879958238384.post-8552201077086864523</id><published>2009-01-04T04:52:00.001-08:00</published><updated>2009-01-05T09:07:29.382-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='functional programming'/><category scheme='http://www.blogger.com/atom/ns#' term='W3R'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>W3R-3: Object-Oriented Reduce</title><content type='html'>We now have &lt;a href="http://appliedabstraction.blogspot.com/2009/01/w3r-2-importing-reduce-into-python.html"&gt;worked through&lt;/a&gt; deriving &lt;code&gt;reduce&lt;/code&gt; in Python. In doing so, we have defined two closely related functions, &lt;code&gt;fold&lt;/code&gt;&lt;a href="#Note1" id="refNote1"&gt;&lt;sup&gt;1&lt;/sup&gt;&lt;/a&gt; and &lt;code&gt;scan&lt;/code&gt;. Since they're so similar, it makes sense to briefly digress and consider bundling them together into a class. I won't make much use of the class in future installments of &lt;a href="http://appliedabstraction.blogspot.com/search/label/W3R"&gt;this series&lt;/a&gt;, but it is an interesting exercise to consider alternative definitions that might be a better fit to Python. &lt;br /&gt;&lt;br /&gt;Define:&lt;pre style="color:#000000; background-color:#fff5ee; font-size:10pt; font-family:'Courier New';"&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;class&lt;/span&gt; &lt;span style="color:#000069"&gt;Fold&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;object&lt;/span&gt;&lt;span style="color:#000000"&gt;):&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    &lt;span style="color:#2e8b57; font-weight:bold"&gt;def&lt;/span&gt; &lt;span style="color:#000069"&gt;__init__&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;self&lt;span style="color:#000000"&gt;,&lt;/span&gt; function&lt;span style="color:#000000"&gt;,&lt;/span&gt; init&lt;span style="color:#000000"&gt;):&lt;/span&gt;&lt;br /&gt;        self&lt;span style="color:#000000"&gt;.&lt;/span&gt;function &lt;span style="color:#000000"&gt;=&lt;/span&gt; function&lt;br /&gt;        self&lt;span style="color:#000000"&gt;.&lt;/span&gt;init &lt;span style="color:#000000"&gt;=&lt;/span&gt; init&lt;br /&gt;&lt;br /&gt;    &lt;span style="color:#2e8b57; font-weight:bold"&gt;def&lt;/span&gt; &lt;span style="color:#000069"&gt;__call__&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;self&lt;span style="color:#000000"&gt;,&lt;/span&gt; acc&lt;span style="color:#000000"&gt;,&lt;/span&gt; x&lt;span style="color:#000000"&gt;):&lt;/span&gt;&lt;br /&gt;        &lt;span style="color:#2e8b57; font-weight:bold"&gt;return&lt;/span&gt; self&lt;span style="color:#000000"&gt;.&lt;/span&gt;&lt;span style="color:#000069"&gt;function&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;acc&lt;span style="color:#000000"&gt;,&lt;/span&gt; x&lt;span style="color:#000000"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    &lt;span style="color:#2e8b57; font-weight:bold"&gt;def&lt;/span&gt; &lt;span style="color:#000069"&gt;scan&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;self&lt;span style="color:#000000"&gt;,&lt;/span&gt; iterable&lt;span style="color:#000000"&gt;):&lt;/span&gt;&lt;br /&gt;        function &lt;span style="color:#000000"&gt;=&lt;/span&gt; self&lt;span style="color:#000000"&gt;.&lt;/span&gt;function&lt;br /&gt;        acc &lt;span style="color:#000000"&gt;=&lt;/span&gt; self&lt;span style="color:#000000"&gt;.&lt;/span&gt;init&lt;br /&gt;        &lt;span style="color:#2e8b57; font-weight:bold"&gt;yield&lt;/span&gt; acc&lt;br /&gt;        &lt;span style="color:#2e8b57; font-weight:bold"&gt;for&lt;/span&gt; x &lt;span style="color:#2e8b57; font-weight:bold"&gt;in&lt;/span&gt; iterable&lt;span style="color:#000000"&gt;:&lt;/span&gt;&lt;br /&gt;            acc &lt;span style="color:#000000"&gt;=&lt;/span&gt; &lt;span style="color:#000069"&gt;function&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;acc&lt;span style="color:#000000"&gt;,&lt;/span&gt; x&lt;span style="color:#000000"&gt;)&lt;/span&gt;&lt;br /&gt;            &lt;span style="color:#2e8b57; font-weight:bold"&gt;yield&lt;/span&gt; acc&lt;br /&gt;&lt;br /&gt;    &lt;span style="color:#2e8b57; font-weight:bold"&gt;def&lt;/span&gt; &lt;span style="color:#000069"&gt;fold&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;self&lt;span style="color:#000000"&gt;,&lt;/span&gt; iterable&lt;span style="color:#000000"&gt;):&lt;/span&gt;&lt;br /&gt;        &lt;span style="color:#2e8b57; font-weight:bold"&gt;for&lt;/span&gt; x &lt;span style="color:#2e8b57; font-weight:bold"&gt;in&lt;/span&gt; self&lt;span style="color:#000000"&gt;.&lt;/span&gt;&lt;span style="color:#000069"&gt;scan&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;iterable&lt;span style="color:#000000"&gt;):&lt;/span&gt; &lt;span style="color:#2e8b57; font-weight:bold"&gt;pass&lt;/span&gt;&lt;br /&gt;        &lt;span style="color:#2e8b57; font-weight:bold"&gt;return&lt;/span&gt; x&lt;br /&gt;&lt;/pre&gt;The &lt;code&gt;Fold&lt;/code&gt; class is defined to take the function and initial value for the accumulator when the class is instantiated. This gives us some of the benefits we saw for the &lt;a href="http://appliedabstraction.blogspot.com/2009/01/w3r-1-origins-of-reduce.html"&gt;curried SML functions&lt;/a&gt;. &lt;br /&gt;&lt;br /&gt;Putting the &lt;code&gt;Fold&lt;/code&gt; class to use, we can define the &lt;code&gt;sum&lt;/code&gt; and &lt;code&gt;cumsum&lt;/code&gt; functions that we saw &lt;a href="http://appliedabstraction.blogspot.com/2009/01/w3r-2-importing-reduce-into-python.html"&gt;last time&lt;/a&gt;:&lt;pre style="color:#000000; background-color:#fff5ee; font-size:10pt; font-family:'Courier New';"&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;sum&lt;/span&gt; &lt;span style="color:#000000"&gt;=&lt;/span&gt; &lt;span style="color:#000069"&gt;Fold&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;operator&lt;span style="color:#000000"&gt;.&lt;/span&gt;add&lt;span style="color:#000000"&gt;,&lt;/span&gt; &lt;span style="color:#ff1493"&gt;0&lt;/span&gt;&lt;span style="color:#000000"&gt;).&lt;/span&gt;fold&lt;br /&gt;cumsum &lt;span style="color:#000000"&gt;=&lt;/span&gt; &lt;span style="color:#000069"&gt;Fold&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;operator&lt;span style="color:#000000"&gt;.&lt;/span&gt;add&lt;span style="color:#000000"&gt;,&lt;/span&gt; &lt;span style="color:#ff1493"&gt;0&lt;/span&gt;&lt;span style="color:#000000"&gt;).&lt;/span&gt;scan&lt;br /&gt;&lt;/pre&gt; Perhaps more useful is to define something like&lt;&lt;pre style="color:#000000; background-color:#fff5ee; font-size:10pt; font-family:'Courier New';"&gt;add &lt;span style="color:#000000"&gt;=&lt;/span&gt; &lt;span style="color:#000069"&gt;Fold&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;operator&lt;span style="color:#000000"&gt;.&lt;/span&gt;add&lt;span style="color:#000000"&gt;,&lt;/span&gt; &lt;span style="color:#ff1493"&gt;0&lt;/span&gt;&lt;span style="color:#000000"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;and then using &lt;code&gt;add.scan&lt;/code&gt; and &lt;code&gt;add.fold&lt;/code&gt; as needed. &lt;br /&gt;&lt;br /&gt;Indeed, any function of two arguments could be given this treatment, so long as a sensible default (or identity) value can be given. Note that the &lt;code&gt;min3&lt;/code&gt; function from last time is not well suited to this sort of treatment, because it has no clear default value. However, the &lt;code&gt;Fold&lt;/code&gt; class presents no additional difficulties over separate &lt;code&gt;fold&lt;/code&gt; and &lt;code&gt;scan&lt;/code&gt; functions. &lt;br /&gt;&lt;br /&gt;&lt;hr /&gt;&lt;br /&gt;&lt;a href="#refNote1" id="Note1"&gt;&lt;sup&gt;1&lt;/sup&gt;&lt;/a&gt; As a reminder, &lt;code&gt;fold&lt;/code&gt; is another name for &lt;code&gt;reduce&lt;/code&gt;. We'll continue to reserve &lt;code&gt;reduce&lt;/code&gt; as the name of the Python built-in function.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3164056879958238384-8552201077086864523?l=appliedabstraction.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://appliedabstraction.blogspot.com/feeds/8552201077086864523/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3164056879958238384&amp;postID=8552201077086864523' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/8552201077086864523'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/8552201077086864523'/><link rel='alternate' type='text/html' href='http://appliedabstraction.blogspot.com/2009/01/w3r-3-object-oriented-reduce.html' title='W3R-3: Object-Oriented Reduce'/><author><name>Michael Barber</name><uri>http://www.blogger.com/profile/15713879666980145844</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3164056879958238384.post-435708479793291555</id><published>2009-01-02T11:01:00.001-08:00</published><updated>2009-01-05T09:05:54.236-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='functional programming'/><category scheme='http://www.blogger.com/atom/ns#' term='W3R'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='Standard ML'/><title type='text'>W3R-2: Importing Reduce Into Python</title><content type='html'>&lt;a href="http://appliedabstraction.blogspot.com/2009/01/w3r-1-origins-of-reduce.html"&gt;Last time&lt;/a&gt;, we took a look at &lt;code&gt;reduce&lt;/code&gt; in Standard ML, where it is known as a &lt;cite&gt;fold&lt;/cite&gt;. Now, we'll translate those folds into Python. Let's keep calling them folds, reserving reduce for describing the existing Python function. &lt;br /&gt;&lt;br /&gt;The main challenge in the translation process is that Python doesn't have the right data types—Python's lists are actually arrays, not (linked) lists. As a first step, introduce two classes &lt;code&gt;Cons&lt;/code&gt; and &lt;code&gt;Nil&lt;/code&gt; to define linked lists:&lt;pre style="color:#000000; background-color:#fff5ee; font-size:10pt; font-family:'Courier New';"&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;class&lt;/span&gt; &lt;span style="color:#000069"&gt;Cons&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;object&lt;/span&gt;&lt;span style="color:#000000"&gt;):&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    &lt;span style="color:#2e8b57; font-weight:bold"&gt;def&lt;/span&gt; &lt;span style="color:#000069"&gt;__init__&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;self&lt;span style="color:#000000"&gt;,&lt;/span&gt; head&lt;span style="color:#000000"&gt;,&lt;/span&gt; tail&lt;span style="color:#000000"&gt;):&lt;/span&gt;&lt;br /&gt;        self&lt;span style="color:#000000"&gt;.&lt;/span&gt;head &lt;span style="color:#000000"&gt;=&lt;/span&gt; head&lt;br /&gt;        self&lt;span style="color:#000000"&gt;.&lt;/span&gt;tail &lt;span style="color:#000000"&gt;=&lt;/span&gt; tail&lt;br /&gt;&lt;br /&gt;    &lt;span style="color:#2e8b57; font-weight:bold"&gt;def&lt;/span&gt; &lt;span style="color:#000069"&gt;__iter__&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;self&lt;span style="color:#000000"&gt;):&lt;/span&gt;&lt;br /&gt;        lnkLst &lt;span style="color:#000000"&gt;=&lt;/span&gt; self&lt;br /&gt;        &lt;span style="color:#2e8b57; font-weight:bold"&gt;while True&lt;/span&gt;&lt;span style="color:#000000"&gt;:&lt;/span&gt;&lt;br /&gt;            &lt;span style="color:#2e8b57; font-weight:bold"&gt;if&lt;/span&gt; lnkLst&lt;span style="color:#000000"&gt;.&lt;/span&gt;&lt;span style="color:#000069"&gt;isNull&lt;/span&gt;&lt;span style="color:#000000"&gt;():&lt;/span&gt;&lt;br /&gt;                &lt;span style="color:#2e8b57; font-weight:bold"&gt;break&lt;/span&gt;&lt;br /&gt;            &lt;span style="color:#2e8b57; font-weight:bold"&gt;yield&lt;/span&gt; lnkLst&lt;span style="color:#000000"&gt;.&lt;/span&gt;head&lt;br /&gt;            lnkLst &lt;span style="color:#000000"&gt;=&lt;/span&gt; lnkLst&lt;span style="color:#000000"&gt;.&lt;/span&gt;tail&lt;br /&gt;&lt;br /&gt;    &lt;span style="color:#2e8b57; font-weight:bold"&gt;def&lt;/span&gt; &lt;span style="color:#000069"&gt;__repr__&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;self&lt;span style="color:#000000"&gt;):&lt;/span&gt;&lt;br /&gt;        &lt;span style="color:#2e8b57; font-weight:bold"&gt;return&lt;/span&gt; &lt;span style="color:#ff1493"&gt;&amp;quot;Cons(%s, %s)&amp;quot;&lt;/span&gt; &lt;span style="color:#000000"&gt;% (&lt;/span&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;repr&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;self&lt;span style="color:#000000"&gt;.&lt;/span&gt;head&lt;span style="color:#000000"&gt;),&lt;/span&gt; &lt;span style="color:#2e8b57; font-weight:bold"&gt;repr&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;self&lt;span style="color:#000000"&gt;.&lt;/span&gt;tail&lt;span style="color:#000000"&gt;))&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    &lt;span style="color:#2e8b57; font-weight:bold"&gt;def&lt;/span&gt; &lt;span style="color:#000069"&gt;isNull&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;self&lt;span style="color:#000000"&gt;):&lt;/span&gt;&lt;br /&gt;        &lt;span style="color:#2e8b57; font-weight:bold"&gt;return False&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;class&lt;/span&gt; &lt;span style="color:#000069"&gt;Nil&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;object&lt;/span&gt;&lt;span style="color:#000000"&gt;):&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    &lt;span style="color:#2e8b57; font-weight:bold"&gt;def&lt;/span&gt; &lt;span style="color:#000069"&gt;__repr__&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;self&lt;span style="color:#000000"&gt;):&lt;/span&gt;&lt;br /&gt;        &lt;span style="color:#2e8b57; font-weight:bold"&gt;return&lt;/span&gt; &lt;span style="color:#ff1493"&gt;&amp;quot;Nil()&amp;quot;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    &lt;span style="color:#2e8b57; font-weight:bold"&gt;def&lt;/span&gt; &lt;span style="color:#000069"&gt;__iter__&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;self&lt;span style="color:#000000"&gt;):&lt;/span&gt;&lt;br /&gt;        &lt;span style="color:#2e8b57; font-weight:bold"&gt;if False&lt;/span&gt;&lt;span style="color:#000000"&gt;:&lt;/span&gt;&lt;br /&gt;            &lt;span style="color:#2e8b57; font-weight:bold"&gt;yield&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    &lt;span style="color:#2e8b57; font-weight:bold"&gt;def&lt;/span&gt; &lt;span style="color:#000069"&gt;isNull&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;self&lt;span style="color:#000000"&gt;):&lt;/span&gt;&lt;br /&gt;        &lt;span style="color:#2e8b57; font-weight:bold"&gt;return True&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;With these classes, we can construct lists in a Lisp-like fashion:&lt;pre style="color:#000000; background-color:#fff5ee; font-size:10pt; font-family:'Courier New';"&gt;&lt;span style="color:#000069"&gt;Cons&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;&lt;span style="color:#ff1493"&gt;0&lt;/span&gt;&lt;span style="color:#000000"&gt;,&lt;/span&gt; &lt;span style="color:#000069"&gt;Cons&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;&lt;span style="color:#ff1493"&gt;1&lt;/span&gt;&lt;span style="color:#000000"&gt;,&lt;/span&gt; &lt;span style="color:#000069"&gt;Cons&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;&lt;span style="color:#ff1493"&gt;2&lt;/span&gt;&lt;span style="color:#000000"&gt;,&lt;/span&gt; &lt;span style="color:#000069"&gt;Cons&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;&lt;span style="color:#ff1493"&gt;3&lt;/span&gt;&lt;span style="color:#000000"&gt;,&lt;/span&gt; &lt;span style="color:#000069"&gt;Cons&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;&lt;span style="color:#ff1493"&gt;4&lt;/span&gt;&lt;span style="color:#000000"&gt;,&lt;/span&gt; &lt;span style="color:#000069"&gt;Nil&lt;/span&gt;&lt;span style="color:#000000"&gt;())))))&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;For convenience, we'll also introduce a function to convert a Python list into a linked list: &lt;pre style="color:#000000; background-color:#fff5ee; font-size:10pt; font-family:'Courier New';"&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;def&lt;/span&gt; &lt;span style="color:#000069"&gt;fromList&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;lst&lt;span style="color:#000000"&gt;):&lt;/span&gt;&lt;br /&gt;    lnkLst &lt;span style="color:#000000"&gt;=&lt;/span&gt; &lt;span style="color:#000069"&gt;Nil&lt;/span&gt;&lt;span style="color:#000000"&gt;()&lt;/span&gt;&lt;br /&gt;    &lt;span style="color:#2e8b57; font-weight:bold"&gt;for&lt;/span&gt; x &lt;span style="color:#2e8b57; font-weight:bold"&gt;in&lt;/span&gt; lst&lt;span style="color:#000000"&gt;[::-&lt;/span&gt;&lt;span style="color:#ff1493"&gt;1&lt;/span&gt;&lt;span style="color:#000000"&gt;]:&lt;/span&gt;&lt;br /&gt;        lnkLst &lt;span style="color:#000000"&gt;=&lt;/span&gt; &lt;span style="color:#000069"&gt;Cons&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;x&lt;span style="color:#000000"&gt;,&lt;/span&gt; lnkLst&lt;span style="color:#000000"&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span style="color:#2e8b57; font-weight:bold"&gt;return&lt;/span&gt; lnkLst&lt;br /&gt;&lt;/pre&gt; Using &lt;code&gt;fromList(range(5))&lt;/code&gt; is a lot easier than nesting &lt;code&gt;Cons&lt;/code&gt; repeatedly, as shown above.&lt;br /&gt;&lt;br /&gt;With linked lists available, it is easy to define a (left) fold:&lt;pre style="color:#000000; background-color:#fff5ee; font-size:10pt; font-family:'Courier New';"&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;def&lt;/span&gt; &lt;span style="color:#000069"&gt;fold_recur&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;function&lt;span style="color:#000000"&gt;,&lt;/span&gt; init&lt;span style="color:#000000"&gt;,&lt;/span&gt; linkedList&lt;span style="color:#000000"&gt;):&lt;/span&gt;&lt;br /&gt;    &lt;span style="color:#2e8b57; font-weight:bold"&gt;if&lt;/span&gt; linkedList&lt;span style="color:#000000"&gt;.&lt;/span&gt;&lt;span style="color:#000069"&gt;isNull&lt;/span&gt;&lt;span style="color:#000000"&gt;():&lt;/span&gt;&lt;br /&gt;        result &lt;span style="color:#000000"&gt;=&lt;/span&gt; init&lt;br /&gt;    &lt;span style="color:#2e8b57; font-weight:bold"&gt;else&lt;/span&gt;&lt;span style="color:#000000"&gt;:&lt;/span&gt;&lt;br /&gt;        acc &lt;span style="color:#000000"&gt;=&lt;/span&gt; &lt;span style="color:#000069"&gt;function&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;init&lt;span style="color:#000000"&gt;,&lt;/span&gt; linkedList&lt;span style="color:#000000"&gt;.&lt;/span&gt;head&lt;span style="color:#000000"&gt;)&lt;/span&gt;&lt;br /&gt;        result &lt;span style="color:#000000"&gt;=&lt;/span&gt; &lt;span style="color:#000069"&gt;fold_recur&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;function&lt;span style="color:#000000"&gt;,&lt;/span&gt; acc&lt;span style="color:#000000"&gt;,&lt;/span&gt; linkedList&lt;span style="color:#000000"&gt;.&lt;/span&gt;tail&lt;span style="color:#000000"&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span style="color:#2e8b57; font-weight:bold"&gt;return&lt;/span&gt; result&lt;br /&gt;&lt;/pre&gt;I'll note two important differences between the &lt;a href="http://appliedabstraction.blogspot.com/2009/01/w3r-1-origins-of-reduce.html"&gt;SML folds&lt;/a&gt; and &lt;code&gt;fold_recur&lt;/code&gt;. First, the arguments to the &lt;code&gt;function&lt;/code&gt; taken by &lt;code&gt;fold_recur&lt;/code&gt; are reversed from those taken by its SML counterpart, in order to match Python's &lt;code&gt;reduce&lt;/code&gt;. Second, and far more importantly, &lt;code&gt;fold_recur&lt;/code&gt; is terribly broken because it is a &lt;a href="http://en.wikipedia.org/wiki/Tail_recursion"&gt;tail recursive function&lt;/a&gt;, but only has Python's broken support for recursion to draw on. Trying to use &lt;code&gt;fold_recur&lt;/code&gt; will cause stack overflows when the list is too long—that is, containing 1000 elements with the default recursion limit&lt;a href="#Note1" id="refNote1"&gt;&lt;sup&gt;1&lt;/sup&gt;&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;To rectify this problem, we need to transform &lt;code&gt;fold_recur&lt;/code&gt; by hand into an imperative version. Fortunately, &lt;code&gt;fold_recur&lt;/code&gt; is constructed in an iterative form, so the transformation is easy:&lt;pre style="color:#000000; background-color:#fff5ee; font-size:10pt; font-family:'Courier New';"&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;def&lt;/span&gt; &lt;span style="color:#000069"&gt;fold_imper&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;function&lt;span style="color:#000000"&gt;,&lt;/span&gt; init&lt;span style="color:#000000"&gt;,&lt;/span&gt; linkedList&lt;span style="color:#000000"&gt;):&lt;/span&gt;&lt;br /&gt;    acc &lt;span style="color:#000000"&gt;=&lt;/span&gt; init&lt;br /&gt;    &lt;span style="color:#2e8b57; font-weight:bold"&gt;while not&lt;/span&gt; linkedList&lt;span style="color:#000000"&gt;.&lt;/span&gt;&lt;span style="color:#000069"&gt;isNull&lt;/span&gt;&lt;span style="color:#000000"&gt;():&lt;/span&gt;&lt;br /&gt;        acc &lt;span style="color:#000000"&gt;=&lt;/span&gt; &lt;span style="color:#000069"&gt;function&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;acc&lt;span style="color:#000000"&gt;,&lt;/span&gt; linkedList&lt;span style="color:#000000"&gt;.&lt;/span&gt;head&lt;span style="color:#000000"&gt;)&lt;/span&gt;&lt;br /&gt;        linkedList &lt;span style="color:#000000"&gt;=&lt;/span&gt; linkedList&lt;span style="color:#000000"&gt;.&lt;/span&gt;tail&lt;br /&gt;    &lt;span style="color:#2e8b57; font-weight:bold"&gt;return&lt;/span&gt; acc&lt;br /&gt;&lt;/pre&gt; With the imperative definition, our fold will not cause a stack overflow. &lt;br /&gt;&lt;br /&gt;Since we've defined linked lists to be iterable, we can still make our fold a bit more elegant:&lt;pre style="color:#000000; background-color:#fff5ee; font-size:10pt; font-family:'Courier New';"&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;def&lt;/span&gt; &lt;span style="color:#000069"&gt;fold_ll_iter&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;function&lt;span style="color:#000000"&gt;,&lt;/span&gt; init&lt;span style="color:#000000"&gt;,&lt;/span&gt; linkedList&lt;span style="color:#000000"&gt;):&lt;/span&gt;&lt;br /&gt;    acc &lt;span style="color:#000000"&gt;=&lt;/span&gt; init&lt;br /&gt;    &lt;span style="color:#2e8b57; font-weight:bold"&gt;for&lt;/span&gt; x &lt;span style="color:#2e8b57; font-weight:bold"&gt;in&lt;/span&gt; linkedList&lt;span style="color:#000000"&gt;:&lt;/span&gt;&lt;br /&gt;        acc &lt;span style="color:#000000"&gt;=&lt;/span&gt; &lt;span style="color:#000069"&gt;function&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;acc&lt;span style="color:#000000"&gt;,&lt;/span&gt; x&lt;span style="color:#000000"&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span style="color:#2e8b57; font-weight:bold"&gt;return&lt;/span&gt; acc&lt;br /&gt;&lt;/pre&gt; Although &lt;code&gt;fold_ll_iter&lt;/code&gt; was obtained by iterating over a linked list, it will work with any iterable. Rewrite it to make that clearer:&lt;pre style="color:#000000; background-color:#fff5ee; font-size:10pt; font-family:'Courier New';"&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;def&lt;/span&gt; &lt;span style="color:#000069"&gt;fold_iter&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;function&lt;span style="color:#000000"&gt;,&lt;/span&gt; init&lt;span style="color:#000000"&gt;,&lt;/span&gt; iterable&lt;span style="color:#000000"&gt;):&lt;/span&gt;&lt;br /&gt;    acc &lt;span style="color:#000000"&gt;=&lt;/span&gt; init&lt;br /&gt;    &lt;span style="color:#2e8b57; font-weight:bold"&gt;for&lt;/span&gt; x &lt;span style="color:#2e8b57; font-weight:bold"&gt;in&lt;/span&gt; iterable&lt;span style="color:#000000"&gt;:&lt;/span&gt;&lt;br /&gt;        acc &lt;span style="color:#000000"&gt;=&lt;/span&gt; &lt;span style="color:#000069"&gt;function&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;acc&lt;span style="color:#000000"&gt;,&lt;/span&gt; x&lt;span style="color:#000000"&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span style="color:#2e8b57; font-weight:bold"&gt;return&lt;/span&gt; acc&lt;br /&gt;&lt;/pre&gt;Through this process, the deconstruction of a data structure has been shifted from SML fold functions to Python iterators. This is unsurprising: in Python, it is quite standard to use iterators to deconstruct data structures. It is not completely equivalent, but covers many cases. &lt;br /&gt;&lt;br /&gt;The iterator approach suggests another modification of fold, which is normally called &lt;a href="http://www.zvon.org/other/haskell/Outputprelude/scanl_f.html"&gt;scan&lt;/a&gt;:&lt;pre style="color:#000000; background-color:#fff5ee; font-size:10pt; font-family:'Courier New';"&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;def&lt;/span&gt; &lt;span style="color:#000069"&gt;scan&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;function&lt;span style="color:#000000"&gt;,&lt;/span&gt; init&lt;span style="color:#000000"&gt;,&lt;/span&gt; iterable&lt;span style="color:#000000"&gt;):&lt;/span&gt;&lt;br /&gt;    acc &lt;span style="color:#000000"&gt;=&lt;/span&gt; init&lt;br /&gt;    &lt;span style="color:#2e8b57; font-weight:bold"&gt;yield&lt;/span&gt; acc&lt;br /&gt;    &lt;span style="color:#2e8b57; font-weight:bold"&gt;for&lt;/span&gt; x &lt;span style="color:#2e8b57; font-weight:bold"&gt;in&lt;/span&gt; iterable&lt;span style="color:#000000"&gt;:&lt;/span&gt;&lt;br /&gt;        acc &lt;span style="color:#000000"&gt;=&lt;/span&gt; &lt;span style="color:#000069"&gt;function&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;acc&lt;span style="color:#000000"&gt;,&lt;/span&gt; x&lt;span style="color:#000000"&gt;)&lt;/span&gt;&lt;br /&gt;        &lt;span style="color:#2e8b57; font-weight:bold"&gt;yield&lt;/span&gt; acc&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Our &lt;code&gt;scan&lt;/code&gt; function processes the list in the same sequence, yielding values accumulated at each step along the way. Using &lt;code&gt;scan&lt;/code&gt;, we define one last version of &lt;code&gt;fold&lt;/code&gt;:&lt;pre style="color:#000000; background-color:#fff5ee; font-size:10pt; font-family:'Courier New';"&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;def&lt;/span&gt; &lt;span style="color:#000069"&gt;fold&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;function&lt;span style="color:#000000"&gt;,&lt;/span&gt; init&lt;span style="color:#000000"&gt;,&lt;/span&gt; iterable&lt;span style="color:#000000"&gt;):&lt;/span&gt;&lt;br /&gt;    &lt;span style="color:#2e8b57; font-weight:bold"&gt;for&lt;/span&gt; x &lt;span style="color:#2e8b57; font-weight:bold"&gt;in&lt;/span&gt; &lt;span style="color:#000069"&gt;scan&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;function&lt;span style="color:#000000"&gt;,&lt;/span&gt; init&lt;span style="color:#000000"&gt;,&lt;/span&gt; iterable&lt;span style="color:#000000"&gt;):&lt;/span&gt; &lt;span style="color:#2e8b57; font-weight:bold"&gt;pass&lt;/span&gt;&lt;br /&gt;    &lt;span style="color:#2e8b57; font-weight:bold"&gt;return&lt;/span&gt; x&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;We can use &lt;code&gt;scan&lt;/code&gt; and &lt;code&gt;fold&lt;/code&gt; to define many useful functions in a simple manner. For example:&lt;pre style="color:#000000; background-color:#fff5ee; font-size:10pt; font-family:'Courier New';"&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;import&lt;/span&gt; operator&lt;br /&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;def&lt;/span&gt; &lt;span style="color:#2e8b57; font-weight:bold"&gt;sum&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;numbers&lt;span style="color:#000000"&gt;):&lt;/span&gt; &lt;span style="color:#2e8b57; font-weight:bold"&gt;return&lt;/span&gt; &lt;span style="color:#000069"&gt;fold&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;operator&lt;span style="color:#000000"&gt;.&lt;/span&gt;add&lt;span style="color:#000000"&gt;,&lt;/span&gt; &lt;span style="color:#ff1493"&gt;0&lt;/span&gt;&lt;span style="color:#000000"&gt;,&lt;/span&gt; numbers&lt;span style="color:#000000"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;def&lt;/span&gt; &lt;span style="color:#000069"&gt;cumsum&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;numbers&lt;span style="color:#000000"&gt;):&lt;/span&gt; &lt;span style="color:#2e8b57; font-weight:bold"&gt;return&lt;/span&gt; &lt;span style="color:#000069"&gt;scan&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;operator&lt;span style="color:#000000"&gt;.&lt;/span&gt;add&lt;span style="color:#000000"&gt;,&lt;/span&gt; &lt;span style="color:#ff1493"&gt;0&lt;/span&gt;&lt;span style="color:#000000"&gt;,&lt;/span&gt; numbers&lt;span style="color:#000000"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;def&lt;/span&gt; &lt;span style="color:#000069"&gt;product&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;numbers&lt;span style="color:#000000"&gt;):&lt;/span&gt; &lt;span style="color:#2e8b57; font-weight:bold"&gt;return&lt;/span&gt; &lt;span style="color:#000069"&gt;fold&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;operator&lt;span style="color:#000000"&gt;.&lt;/span&gt;mul&lt;span style="color:#000000"&gt;,&lt;/span&gt; &lt;span style="color:#ff1493"&gt;1&lt;/span&gt;&lt;span style="color:#000000"&gt;,&lt;/span&gt; numbers&lt;span style="color:#000000"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;def&lt;/span&gt; &lt;span style="color:#000069"&gt;cumproduct&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;numbers&lt;span style="color:#000000"&gt;):&lt;/span&gt; &lt;span style="color:#2e8b57; font-weight:bold"&gt;return&lt;/span&gt; &lt;span style="color:#000069"&gt;scan&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;operator&lt;span style="color:#000000"&gt;.&lt;/span&gt;mul&lt;span style="color:#000000"&gt;,&lt;/span&gt; &lt;span style="color:#ff1493"&gt;1&lt;/span&gt;&lt;span style="color:#000000"&gt;,&lt;/span&gt; numbers&lt;span style="color:#000000"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt; I think these functions illustrate rather well the difference between &lt;code&gt;fold&lt;/code&gt; and &lt;code&gt;scan&lt;/code&gt;. In &lt;code&gt;sum&lt;/code&gt;, we use &lt;code&gt;fold&lt;/code&gt; to total up the numbers in a list, while in &lt;code&gt;cumsum&lt;/code&gt; we use &lt;code&gt;scan&lt;/code&gt; to generate the partial sums as we proceed through the list. The &lt;code&gt;product&lt;/code&gt; and &lt;code&gt;cumproduct&lt;/code&gt; functions are similar. &lt;br /&gt;&lt;br /&gt;More complex functions are possible as well. Consider finding the three smallest items in a list. This can be done in linear time by iterating through the list and keeping track of the three smallest as we proceed. But that's exactly what &lt;code&gt;fold&lt;/code&gt; is for! The accumulator is a collection of the three smallest items, and we just need a function to update that. Putting that together:&lt;pre style="color:#000000; background-color:#fff5ee; font-size:10pt; font-family:'Courier New';"&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;def&lt;/span&gt; &lt;span style="color:#000069"&gt;min3&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;lst&lt;span style="color:#000000"&gt;):&lt;/span&gt;&lt;br /&gt;    &lt;span style="color:#2e8b57; font-weight:bold"&gt;def&lt;/span&gt; &lt;span style="color:#000069"&gt;comp&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;acc&lt;span style="color:#000000"&gt;,&lt;/span&gt; x&lt;span style="color:#000000"&gt;):&lt;/span&gt;&lt;br /&gt;        a&lt;span style="color:#000000"&gt;,&lt;/span&gt;b&lt;span style="color:#000000"&gt;,&lt;/span&gt;c &lt;span style="color:#000000"&gt;=&lt;/span&gt; acc&lt;br /&gt;        &lt;span style="color:#2e8b57; font-weight:bold"&gt;if&lt;/span&gt; x &lt;span style="color:#000000"&gt;&amp;lt;&lt;/span&gt; a&lt;span style="color:#000000"&gt;:&lt;/span&gt;&lt;br /&gt;            result &lt;span style="color:#000000"&gt;=&lt;/span&gt; x&lt;span style="color:#000000"&gt;,&lt;/span&gt;a&lt;span style="color:#000000"&gt;,&lt;/span&gt;b&lt;br /&gt;        &lt;span style="color:#2e8b57; font-weight:bold"&gt;elif&lt;/span&gt; x &lt;span style="color:#000000"&gt;&amp;lt;&lt;/span&gt; b&lt;span style="color:#000000"&gt;:&lt;/span&gt;&lt;br /&gt;            result &lt;span style="color:#000000"&gt;=&lt;/span&gt; a&lt;span style="color:#000000"&gt;,&lt;/span&gt;x&lt;span style="color:#000000"&gt;,&lt;/span&gt;b&lt;br /&gt;        &lt;span style="color:#2e8b57; font-weight:bold"&gt;elif&lt;/span&gt; x &lt;span style="color:#000000"&gt;&amp;lt;&lt;/span&gt; c&lt;span style="color:#000000"&gt;:&lt;/span&gt;&lt;br /&gt;            result &lt;span style="color:#000000"&gt;=&lt;/span&gt; a&lt;span style="color:#000000"&gt;,&lt;/span&gt;b&lt;span style="color:#000000"&gt;,&lt;/span&gt;x&lt;br /&gt;        &lt;span style="color:#2e8b57; font-weight:bold"&gt;else&lt;/span&gt;&lt;span style="color:#000000"&gt;:&lt;/span&gt;&lt;br /&gt;            result &lt;span style="color:#000000"&gt;=&lt;/span&gt; acc&lt;br /&gt;        &lt;span style="color:#2e8b57; font-weight:bold"&gt;return&lt;/span&gt; result&lt;br /&gt;    init &lt;span style="color:#000000"&gt;=&lt;/span&gt; &lt;span style="color:#2e8b57; font-weight:bold"&gt;sorted&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;lst&lt;span style="color:#000000"&gt;[:&lt;/span&gt;&lt;span style="color:#ff1493"&gt;3&lt;/span&gt;&lt;span style="color:#000000"&gt;])&lt;/span&gt;&lt;br /&gt;    &lt;span style="color:#2e8b57; font-weight:bold"&gt;return&lt;/span&gt; &lt;span style="color:#000069"&gt;fold&lt;/span&gt;&lt;span style="color:#000000"&gt;(&lt;/span&gt;comp&lt;span style="color:#000000"&gt;,&lt;/span&gt; init&lt;span style="color:#000000"&gt;,&lt;/span&gt; lst&lt;span style="color:#000000"&gt;[&lt;/span&gt;&lt;span style="color:#ff1493"&gt;3&lt;/span&gt;&lt;span style="color:#000000"&gt;:])&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;For simplicity, I've assumed that &lt;code&gt;min3&lt;/code&gt; takes a list, but it could easily be modified to work with any iterable. &lt;br /&gt;&lt;br /&gt;I've presented a lot of program code in this post, transforming a direct translation of the SML folds into function that are defined in a more natural way for Python. Next time, I'll digress briefly to present an alternative structure that has some practical advantages over what's been shown here, then return to examining why &lt;code&gt;reduce&lt;/code&gt; doesn't see much use in Python.&lt;br /&gt;&lt;br /&gt;&lt;hr/&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="#refNote1" id="Note1"&gt;&lt;sup&gt;1&lt;/sup&gt;&lt;/a&gt; In practice, this might as well read that the limit is 1000 elements, end of story. The limit can be increased with &lt;code&gt;sys.setrecursionlimit&lt;/code&gt;, but "&lt;a href="http://docs.python.org/library/sys.html"&gt;a too-high limit can lead to a crash.&lt;/a&gt;"&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3164056879958238384-435708479793291555?l=appliedabstraction.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://appliedabstraction.blogspot.com/feeds/435708479793291555/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3164056879958238384&amp;postID=435708479793291555' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/435708479793291555'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/435708479793291555'/><link rel='alternate' type='text/html' href='http://appliedabstraction.blogspot.com/2009/01/w3r-2-importing-reduce-into-python.html' title='W3R-2: Importing Reduce Into Python'/><author><name>Michael Barber</name><uri>http://www.blogger.com/profile/15713879666980145844</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3164056879958238384.post-2987353122995001388</id><published>2009-01-01T14:04:00.001-08:00</published><updated>2009-01-01T14:13:00.126-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='functional programming'/><category scheme='http://www.blogger.com/atom/ns#' term='W3R'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='abstraction'/><category scheme='http://www.blogger.com/atom/ns#' term='Standard ML'/><title type='text'>W3R-1: Origins of Reduce</title><content type='html'>To begin our investigation of &lt;code&gt;reduce&lt;/code&gt; in Python, we need to come to grips with what &lt;code&gt;reduce&lt;/code&gt; actually is. I find that the documentation for &lt;code&gt;reduce&lt;/code&gt; is rather poor, requiring a fair amount of thinking to determine just what you need to pass in as arguments. Instead, let's examine the origins of &lt;code&gt;reduce&lt;/code&gt; to better understand it.&lt;br /&gt;&lt;br /&gt;&lt;code&gt;Reduce&lt;/code&gt; originates outside of Python. It comes to Python through a contributed patch: &lt;blockquote&gt;About 12 years ago, Python aquired lambda, reduce(), filter() and map(), courtesy of (I believe) a Lisp hacker who missed them and submitted working patches. &lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.artima.com/weblogs/viewpost.jsp?thread=98196"&gt;The fate of reduce() in Python 3000&lt;/a&gt;&lt;br /&gt;Guido van Rossum, 2005&lt;/blockquote&gt; That's not very helpful, so let's derive &lt;code&gt;reduce&lt;/code&gt; by abstracting from some examples. Instead of Lisp, I'll use &lt;a href="http://www.smlnj.org/sml.html"&gt;Standard ML&lt;/a&gt;. SML has two key advantages over Lisp: the syntax is more approachable for the uninitiated, and its robust (&lt;a href="http://www.codecommit.com/blog/scala/what-is-hindley-milner-and-why-is-it-cool"&gt;Hindley-Milner&lt;/a&gt;) type system and pattern matching will make things a little clearer. I'll follow the definitions in SML rather than Python, so we'll refer to &lt;code&gt;reduce&lt;/code&gt;-like functions as &lt;cite&gt;folds&lt;/cite&gt;, with argument order matching those in the &lt;a href="http://sml.sourceforge.net/Basis/list.html"&gt;Standard ML Basis Library&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Consider adding up a list of integers. In SML, that list is a linked list, so we'll have a typical recursive solution:&lt;pre style="color:#000000; background-color:#fff5ee; font-size:10pt; font-family:'Courier New';"&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;fun&lt;/span&gt; sum &lt;span style="color:#000000"&gt;[] =&lt;/span&gt; &lt;span style="color:#ff1493"&gt;0&lt;/span&gt;&lt;br /&gt;  &lt;span style="color:#000000"&gt;|&lt;/span&gt; sum &lt;span style="color:#000000"&gt;(&lt;/span&gt;h&lt;span style="color:#000000"&gt;::&lt;/span&gt;t&lt;span style="color:#000000"&gt;) =&lt;/span&gt; h &lt;span style="color:#000000"&gt;+&lt;/span&gt; sum t&lt;br /&gt;&lt;/pre&gt; I've made use of pattern matching to define &lt;code&gt;sum&lt;/code&gt; in terms of two cases, that of an empty list and of a non-empty list. The double colon is a &lt;a href="http://en.wikipedia.org/wiki/Cons"&gt;cons&lt;/a&gt; operator, joining the head &lt;code&gt;h&lt;/code&gt; and tail &lt;code&gt;t&lt;/code&gt; of the list.&lt;br /&gt;&lt;br /&gt;To define &lt;code&gt;map&lt;/code&gt;, a similar recursion scheme is used:&lt;pre style="color:#000000; background-color:#fff5ee; font-size:10pt; font-family:'Courier New';"&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;fun&lt;/span&gt; map f &lt;span style="color:#000000"&gt;[] = []&lt;/span&gt;&lt;br /&gt;  &lt;span style="color:#000000"&gt;|&lt;/span&gt; map f &lt;span style="color:#000000"&gt;(&lt;/span&gt;h&lt;span style="color:#000000"&gt;::&lt;/span&gt;t&lt;span style="color:#000000"&gt;) = (&lt;/span&gt;f h&lt;span style="color:#000000"&gt;) :: (&lt;/span&gt;map f t&lt;span style="color:#000000"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt; &lt;code&gt;Map&lt;/code&gt; is written as a &lt;a href="http://www.haskell.org/haskellwiki/Currying"&gt;curried function&lt;/a&gt;, so different list processors can be defined by specifying just the function &lt;code&gt;map f&lt;/code&gt;, only giving the list later.&lt;a href="#Note1" id="refNote1"&gt;&lt;sup&gt;1&lt;/sup&gt;&lt;/a&gt; &lt;br /&gt;&lt;br /&gt;Just about any function over a list uses the same recursion scheme. Let's extract that recursions scheme into a higher order function called &lt;code&gt;foldr&lt;/code&gt;:&lt;pre style="color:#000000; background-color:#fff5ee; font-size:10pt; font-family:'Courier New';"&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;fun&lt;/span&gt; foldr f acc &lt;span style="color:#000000"&gt;[] =&lt;/span&gt; acc&lt;br /&gt;  &lt;span style="color:#000000"&gt;|&lt;/span&gt; foldr f acc &lt;span style="color:#000000"&gt;(&lt;/span&gt;h&lt;span style="color:#000000"&gt;::&lt;/span&gt;t&lt;span style="color:#000000"&gt;) =&lt;/span&gt; f&lt;span style="color:#000000"&gt;(&lt;/span&gt;h&lt;span style="color:#000000"&gt;, (&lt;/span&gt;foldr f acc t&lt;span style="color:#000000"&gt;))&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt; &lt;code&gt;Foldr&lt;/code&gt; deconstructs a list from right to left, starting with the tail and accumulating the values. As with &lt;code&gt;map&lt;/code&gt;, &lt;code&gt;foldr&lt;/code&gt; is a curried function. &lt;br /&gt;&lt;br /&gt;The type of &lt;code&gt;foldr&lt;/code&gt; can make it easier to understand:&lt;pre style="color:#000000; background-color:#fff5ee; font-size:10pt; font-family:'Courier New';"&gt;&lt;span style="color:#000000"&gt;('&lt;/span&gt;a &lt;span style="color:#000000"&gt;* '&lt;/span&gt;b &lt;span style="color:#000000"&gt;-&amp;gt; '&lt;/span&gt;b&lt;span style="color:#000000"&gt;) -&amp;gt; '&lt;/span&gt;b &lt;span style="color:#000000"&gt;-&amp;gt; '&lt;/span&gt;a list &lt;span style="color:#000000"&gt;-&amp;gt; '&lt;/span&gt;b&lt;br /&gt;&lt;/pre&gt; The first term, in the parentheses, describes the function &lt;code&gt;f&lt;/code&gt;; it takes a list element and an accumulator, returning an updated accumulator. The second argument to &lt;code&gt;foldr&lt;/code&gt; is an initial value for the accumulator. Third is the list to process. Importantly, the types of the accumulator and the list elements can differ. &lt;br /&gt;&lt;br /&gt;We can use &lt;code&gt;foldr&lt;/code&gt; to define &lt;code&gt;sum&lt;/code&gt; and &lt;code&gt;map&lt;/code&gt;. Here's &lt;code&gt;sum&lt;/code&gt;:&lt;pre style="color:#000000; background-color:#fff5ee; font-size:10pt; font-family:'Courier New';"&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;val&lt;/span&gt; sum &lt;span style="color:#000000"&gt;=&lt;/span&gt; foldr &lt;span style="color:#2e8b57; font-weight:bold"&gt;Int&lt;/span&gt;.&lt;span style="color:#000000"&gt;+&lt;/span&gt; &lt;span style="color:#ff1493"&gt;0&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt; For &lt;code&gt;map&lt;/code&gt;, we'll introduce a helper function and use &lt;code&gt;foldr&lt;/code&gt; on that:&lt;pre style="color:#000000; background-color:#fff5ee; font-size:10pt; font-family:'Courier New';"&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;fun&lt;/span&gt; map f lst &lt;span style="color:#000000"&gt;=&lt;/span&gt; &lt;span style="color:#2e8b57; font-weight:bold"&gt;let&lt;/span&gt;&lt;br /&gt;  &lt;span style="color:#2e8b57; font-weight:bold"&gt;fun&lt;/span&gt; maphead&lt;span style="color:#000000"&gt;(&lt;/span&gt;x&lt;span style="color:#000000"&gt;,&lt;/span&gt; acc&lt;span style="color:#000000"&gt;) = (&lt;/span&gt;f x&lt;span style="color:#000000"&gt;) ::&lt;/span&gt; acc&lt;br /&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;in&lt;/span&gt;&lt;br /&gt;  foldr maphead &lt;span style="color:#000000"&gt;[]&lt;/span&gt; lst&lt;br /&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt; To get the helper function &lt;code&gt;maphead&lt;/code&gt; right, I just looked at the types needed for &lt;code&gt;foldr&lt;/code&gt; and wrote the obvious function to satisfy those types. &lt;br /&gt;&lt;br /&gt;While &lt;code&gt;foldr&lt;/code&gt; allows elegant definition of our two example functions, there is a problem. As written, &lt;code&gt;foldr&lt;/code&gt; walks through the entire list before starting to do any work. If the list is long, this can cause a stack overflow. We need a &lt;a href="http://en.wikipedia.org/wiki/Tail_recursion"&gt;tail-recursive&lt;/a&gt; version, leading us naturally to &lt;code&gt;foldl&lt;/code&gt;:&lt;pre style="color:#000000; background-color:#fff5ee; font-size:10pt; font-family:'Courier New';"&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;fun&lt;/span&gt; foldl f acc &lt;span style="color:#000000"&gt;[] =&lt;/span&gt; acc&lt;br /&gt;  &lt;span style="color:#000000"&gt;|&lt;/span&gt; foldl f acc &lt;span style="color:#000000"&gt;(&lt;/span&gt;h&lt;span style="color:#000000"&gt;::&lt;/span&gt;t&lt;span style="color:#000000"&gt;) =&lt;/span&gt; foldl f &lt;span style="color:#000000"&gt;(&lt;/span&gt;f&lt;span style="color:#000000"&gt;(&lt;/span&gt;h&lt;span style="color:#000000"&gt;,&lt;/span&gt; acc&lt;span style="color:#000000"&gt;))&lt;/span&gt; t&lt;br /&gt;&lt;/pre&gt;In contrast to &lt;code&gt;foldr&lt;/code&gt;, &lt;code&gt;foldl&lt;/code&gt; processes the list from left to right, starting with the head. This definition of &lt;code&gt;foldl&lt;/code&gt; is tail recursive, so operates in constant space. It can also be used to define a tail recursive &lt;code&gt;foldr&lt;/code&gt; by reversing the list before passing it to &lt;code&gt;foldl&lt;/code&gt;. &lt;br /&gt;&lt;br /&gt;&lt;code&gt;Foldl&lt;/code&gt; is substantially similar to &lt;code&gt;reduce&lt;/code&gt; in Python. Next time, I'll translate the above definitions to Python, to make the connection transparent.&lt;br /&gt;&lt;br /&gt;&lt;hr/&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="#refNote1" id="Note1"&gt;&lt;sup&gt;1&lt;/sup&gt;&lt;/a&gt; Having a curried &lt;code&gt;map&lt;/code&gt; in SML makes for a big conceptual difference from the &lt;code&gt;map&lt;/code&gt; in Python. It's quite reasonable to view &lt;code&gt;map&lt;/code&gt; as transforming a function from acting on values to a function acting on lists of values, without &lt;code&gt;map&lt;/code&gt; itself actually having anything to do with the list. &lt;br /&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3164056879958238384-2987353122995001388?l=appliedabstraction.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://appliedabstraction.blogspot.com/feeds/2987353122995001388/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3164056879958238384&amp;postID=2987353122995001388' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/2987353122995001388'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/2987353122995001388'/><link rel='alternate' type='text/html' href='http://appliedabstraction.blogspot.com/2009/01/w3r-1-origins-of-reduce.html' title='W3R-1: Origins of Reduce'/><author><name>Michael Barber</name><uri>http://www.blogger.com/profile/15713879666980145844</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3164056879958238384.post-1719651114758821018</id><published>2008-12-31T07:46:00.001-08:00</published><updated>2009-01-01T11:40:53.077-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='functional programming'/><category scheme='http://www.blogger.com/atom/ns#' term='W3R'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>What's Wrong With Reduce?</title><content type='html'>Functional programming seems to be on the rise—there's even an O'Reilly book on Haskell, now. I'm not quite sure how that increase in popularity happened, but would guess that Paul Graham's &lt;a href="http://www.paulgraham.com/avg.html"&gt;advocacy of Lisp&lt;/a&gt; had something to do with it, as did Douglas Crockford's &lt;a href="http://javascript.crockford.com/javascript.html"&gt;work with JavaScript&lt;/a&gt;. I also suspect that the higher-order constructs in Python played an important role, allowing imperative programmers to gradually start using a functional style.&lt;br /&gt;&lt;br /&gt;However, there is something curious about the functional  constructs in Python. One of the higher-order functions in Python—&lt;code&gt;reduce&lt;/code&gt;—seems not to find much acceptance. Probably the best example of this is from the &lt;a href="http://en.wikipedia.org/wiki/Guido_van_Rossum"&gt;BDFL&lt;/a&gt; himself, who wrote: &lt;br /&gt;&lt;blockquote&gt;…almost every time I see a reduce() call with a non-trivial function argument, I need to grab pen and paper to diagram what's actually being fed into that function before I understand what the reduce() is supposed to do. So in my mind, the applicability of reduce() is pretty much limited to associative operators, and in all other cases it's better to write out the accumulation loop explicitly. &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.artima.com/weblogs/viewpost.jsp?thread=98196"&gt;The fate of reduce() in Python 3000&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Guido van Rossum, 2005&lt;/blockquote&gt;&lt;br /&gt;I don't think the above is out of line with most Python users think, but it clashes severely with conventional thinking of users of functional programming languages. &lt;br /&gt;&lt;br /&gt;For example, in an influential paper, &lt;a href="http://en.wikipedia.org/wiki/John_Hughes_(computer_scientist)"&gt;John Hughes&lt;/a&gt; wrote&lt;br /&gt;&lt;blockquote&gt;…a little modularisation can go a long way. By modularising a simple function (sum) as a combination of a “higher order function” and some simple arguments, we have arrived at a part (reduce) that can be used to write down many other functions on lists with no more programming eﬀort.&lt;/blockquote&gt; and &lt;blockquote&gt;…functional languages allow functions which are indivisible in conventional programming languages to be expressed as a combination of parts—a general higher order function and some particular specialising functions. Once deﬁned, such higher order functions allow many operations to be programmed very easily. Whenever a new datatype is deﬁned higher order functions should be written for processing it. This makes manipulating the datatype easy, and also localises knowledge about the details of its representation. The best analogy with conventional programming is with extensible languages—it is as though the programming language can be extended with new control structures whenever desired. &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.cs.chalmers.se/~rjmh/Papers/whyfp.html"&gt;Why Functional Programming Matters&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;John Hughes, 1984&lt;/blockquote&gt;&lt;br /&gt;My own experience with &lt;a href="http://en.wikipedia.org/wiki/Standard_ML"&gt;ML&lt;/a&gt; is quite in line with what Hughes wrote: &lt;code&gt;reduce&lt;/code&gt;, or &lt;code&gt;foldl&lt;/code&gt; as it is also known, is used all the time, and seems very natural. &lt;br /&gt;&lt;br /&gt;Why the difference? I'm not aware of any case where GvR has demonstrated much knowledge of functional programming—maybe he was just speaking outside his area of expertise, and got it wrong? The &lt;a href="http://www.plt-scheme.org/"&gt;PLT Scheme&lt;/a&gt; people may have thought so, &lt;a href="http://list.cs.brown.edu/pipermail/plt-scheme/2005-April/008382.html"&gt;satirizing &lt;/a&gt; him shortly after in a (quite funny!) April Fool's Day announcement. &lt;br /&gt;&lt;br /&gt;However, I think that explanation is overly facile. Personally, while I use folds in functional languages, I rarely do in Python. I think there are real reasons for that, and will examine them in future blog posts.&lt;br /&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3164056879958238384-1719651114758821018?l=appliedabstraction.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://appliedabstraction.blogspot.com/feeds/1719651114758821018/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3164056879958238384&amp;postID=1719651114758821018' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/1719651114758821018'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/1719651114758821018'/><link rel='alternate' type='text/html' href='http://appliedabstraction.blogspot.com/2008/12/what-wrong-with-reduce.html' title='What&amp;#39;s Wrong With Reduce?'/><author><name>Michael Barber</name><uri>http://www.blogger.com/profile/15713879666980145844</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3164056879958238384.post-306525453557391760</id><published>2008-12-10T03:01:00.001-08:00</published><updated>2008-12-10T03:01:00.470-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='heavy tail'/><category scheme='http://www.blogger.com/atom/ns#' term='society'/><category scheme='http://www.blogger.com/atom/ns#' term='distribution'/><category scheme='http://www.blogger.com/atom/ns#' term='science'/><category scheme='http://www.blogger.com/atom/ns#' term='power law'/><title type='text'>Power Laws and Social Problems</title><content type='html'>&lt;a href="http://www.gladwell.com/2006/2006_02_13_a_murray.html"&gt;Million-Dollar Murray&lt;/a&gt; is a fascinating article by &lt;a href="http://www.gladwell.com/"&gt;Malcolm Gladwell&lt;/a&gt; about how "power laws" (really, heavy tailed distributions) can have surprising policy implications for dealing with social problems. &lt;br /&gt;&lt;br /&gt;The article focuses most strongly on homelessness. The core of how power laws clash with our assumptions is summarized as:&lt;blockquote&gt;That is what is so perplexing about power-law homeless policy. From an economic perspective the approach makes perfect sense. But from a moral perspective it doesn't seem fair. Thousands of people in the Denver area no doubt live day to day, work two or three jobs, and are eminently deserving of a helping hand—and no one offers them the key to a new apartment. […] Social benefits are supposed to have some kind of moral justification. We give them to widows and disabled veterans and poor mothers with small children. Giving the homeless guy passed out on the sidewalk an apartment has a different rationale. It's simply about efficiency.&lt;/blockquote&gt;&lt;br /&gt;Another issue considered is police brutality. After the Rodney King beating in Los Angeles, a special commission headed by Warren Christopher investigated the LAPD: &lt;blockquote&gt;But what was the commission's most memorable observation? It was the story of an officer with a known history of doing things like beating up handcuffed suspects who nonetheless received a performance review from his superior stating that he "usually conducts himself in a manner that inspires respect for the law and instills public confidence." This is what you say about an officer when you haven't actually read his file, and the implication of the Christopher Commission's report was that the L.A.P.D. might help solve its problem simply by getting its police captains to read the files of their officers. The L.A.P.D.'s problem was a matter not of policy but of compliance. The department needed to adhere to the rules it already had in place, and that's not what a public hungry for institutional transformation wants to hear. Solving problems that have power-law distributions doesn't just violate our moral intuitions; it violates our political intuitions as well.&lt;/blockquote&gt;&lt;br /&gt;The third issue explored is pollution by automobile exhaust. This seems quite prosaic in comparison to the first two, but may be the best illustration of the way that we can incorrectly address a problem if we have an erroneous view of the distribution of the problem. For cars, we have:&lt;blockquote&gt;Most cars, especially new ones, are extraordinarily clean. A 2004 Subaru in good working order has an exhaust stream that's just .06 per cent carbon monoxide, which is negligible. But on almost any highway, for whatever reason—age, ill repair, deliberate tampering by the owner—a small number of cars can have carbon-monoxide levels in excess of ten per cent, which is almost two hundred times higher. In Denver, five per cent of the vehicles on the road produce fifty-five per cent of the automobile pollution.&lt;/blockquote&gt; Given this heavy tailed distribution, alternatives to the customary annual inspection become more relevant: &lt;blockquote&gt;[Donald Stedman, a chemist and automobile-emissions specialist at the University of Denver] proposes mobile testing instead. Twenty years ago, he invented a device the size of a suitcase that uses infrared light to instantly measure and then analyze the emissions of cars as they drive by on the highway. […] He says that cities should put half a dozen or so of his devices in vans, park them on freeway off-ramps around the city, and have a police car poised to pull over anyone who fails the test. A half-dozen vans could test thirty thousand cars a day. For the same twenty-five million dollars that Denver's motorists now spend on on-site testing, Stedman estimates, the city could identify and fix twenty-five thousand truly dirty vehicles every year, and within a few years cut automobile emissions in the Denver metropolitan area by somewhere between thirty-five and forty per cent. The city could stop managing its smog problem and start ending it.&lt;/blockquote&gt; Not mentioned by Gladwell is that this mobile testing would also have a moral benefit: costs could be shifted to polluters, instead of spreading the cost to everyone. &lt;br /&gt;&lt;br /&gt;In summary, a fascinating article that connects a current scientific issue with current societal issues. I'm sure it only scratches the surface. No doubt, many other issues depend crucially on the distribution of underlying factors. &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3164056879958238384-306525453557391760?l=appliedabstraction.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://appliedabstraction.blogspot.com/feeds/306525453557391760/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3164056879958238384&amp;postID=306525453557391760' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/306525453557391760'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/306525453557391760'/><link rel='alternate' type='text/html' href='http://appliedabstraction.blogspot.com/2008/12/power-laws-and-social-problems.html' title='Power Laws and Social Problems'/><author><name>Michael Barber</name><uri>http://www.blogger.com/profile/15713879666980145844</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3164056879958238384.post-7494314523614961148</id><published>2008-12-08T13:20:00.001-08:00</published><updated>2008-12-08T13:20:16.442-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='MarsEdit'/><category scheme='http://www.blogger.com/atom/ns#' term='blogging'/><category scheme='http://www.blogger.com/atom/ns#' term='SubEthaEdit'/><category scheme='http://www.blogger.com/atom/ns#' term='shell'/><title type='text'>Formatted Program Text for Blogger</title><content type='html'>This blog features a fair amount of code. So far, I've been doing the formatting from &lt;a href="http://codingmonkeys.de/subethaedit"&gt;SubEthaEdit&lt;/a&gt;. Using SEE has worked OK, but was kind of a fiddly procedure. I'd open the desired program, copy as XHTML, paste that into a new document, remove all the lines breaks, copy the modified text, and paste it into &lt;a href="http://www.red-sweater.com/marsedit/"&gt;MarsEdit&lt;/a&gt;. No step is difficult, but automating it is a pain, requiring GUI scripting in AppleScript. Further, it would require running everything through SEE, whether that would make sense or not.&lt;br /&gt;&lt;br /&gt;Thus, before scripting the above approach, I looked for other options. Two seem promising: &lt;a href="http://pygments.org/"&gt;Pygments&lt;/a&gt; and &lt;a href="http://www.andre-simon.de/doku/highlight/en/highlight.html"&gt;highlight&lt;/a&gt;. Both provide shell filters for converting program text into HTML. For now, I've settled on highlight, since Pygments lacks support for highlighting Standard ML. &lt;br /&gt;&lt;br /&gt;Several command line options are appropriate. For example, I can format an SML file with &lt;pre&gt;cat real-ord.sml | highlight --inline-css -f --enclose-pre -S sml -s seashell | pbcopy &lt;/pre&gt;&lt;br /&gt;Pasting that into MarsEdit nicely formats the contents of &lt;code&gt;real-ord.sml&lt;/code&gt;:&lt;br /&gt;&lt;pre style="color:#000000; background-color:#fff5ee; font-size:10pt; font-family:'Courier New';"&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;structure&lt;/span&gt; RealOrdKey &lt;span style="color:#000000"&gt;:&lt;/span&gt; ORD_KEY &lt;span style="color:#2e8b57; font-weight:bold"&gt;where type&lt;/span&gt; ord_key &lt;span style="color:#000000"&gt;=&lt;/span&gt; &lt;span style="color:#2e8b57; font-weight:bold"&gt;Real&lt;/span&gt;.&lt;span style="color:#2e8b57; font-weight:bold"&gt;real&lt;/span&gt; &lt;span style="color:#000000"&gt;=&lt;/span&gt; &lt;span style="color:#2e8b57; font-weight:bold"&gt;struct&lt;/span&gt;&lt;br /&gt;  &lt;span style="color:#2e8b57; font-weight:bold"&gt;open Real&lt;/span&gt;&lt;br /&gt;  &lt;span style="color:#2e8b57; font-weight:bold"&gt;type&lt;/span&gt; ord_key &lt;span style="color:#000000"&gt;=&lt;/span&gt; &lt;span style="color:#2e8b57; font-weight:bold"&gt;real&lt;/span&gt;&lt;br /&gt;&lt;span style="color:#2e8b57; font-weight:bold"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;I'm sure I'll need to refine the approach a bit as I go, but this already seems easier than the approach I'd been using.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3164056879958238384-7494314523614961148?l=appliedabstraction.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://appliedabstraction.blogspot.com/feeds/7494314523614961148/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3164056879958238384&amp;postID=7494314523614961148' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/7494314523614961148'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/7494314523614961148'/><link rel='alternate' type='text/html' href='http://appliedabstraction.blogspot.com/2008/12/formatted-program-text-for-blogger.html' title='Formatted Program Text for Blogger'/><author><name>Michael Barber</name><uri>http://www.blogger.com/profile/15713879666980145844</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3164056879958238384.post-6037262727693235174</id><published>2008-12-08T09:00:00.001-08:00</published><updated>2008-12-20T11:34:55.287-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>Another Skeptical Take on Python 3</title><content type='html'>Jens Alfke wonders &lt;a href="http://mooseyard.com/Jens/2008/12/python-30-whats-the-point/"&gt;"What’s The Point?"&lt;/a&gt; of Python 3.&lt;br /&gt;&lt;br /&gt;Via &lt;a href="http://daringfireball.net/"&gt;Daring Fireball&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3164056879958238384-6037262727693235174?l=appliedabstraction.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://appliedabstraction.blogspot.com/feeds/6037262727693235174/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3164056879958238384&amp;postID=6037262727693235174' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/6037262727693235174'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/6037262727693235174'/><link rel='alternate' type='text/html' href='http://appliedabstraction.blogspot.com/2008/12/another-skeptical-take-on-python-3.html' title='Another Skeptical Take on Python 3'/><author><name>Michael Barber</name><uri>http://www.blogger.com/profile/15713879666980145844</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3164056879958238384.post-7481652884352693250</id><published>2008-12-05T09:00:00.001-08:00</published><updated>2008-12-05T09:22:09.210-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='functional programming'/><category scheme='http://www.blogger.com/atom/ns#' term='types'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><category scheme='http://www.blogger.com/atom/ns#' term='concurrency'/><title type='text'>Thoughts on Python 3.0</title><content type='html'>&lt;a href="http://www.python.org/download/releases/3.0/"&gt;Python 3.0&lt;/a&gt; has been released. I first used Python at either the end of 1999 or the beginning of 2000. It's with some surprise that I find myself rather ambivalent about what is a major landmark for a tool that I've used on a near-daily basis for almost nine years.&lt;br /&gt;&lt;br /&gt;There are a variety of reasons for my ambivalence. Largely, my own programming interests have diverged from the main features of Python. Thus, the significant changes that are present in Python 3 just aren't addressing the shortcomings of Python that affect me. In particular, &lt;ul&gt; &lt;li&gt;My interest in functional programming has continually increased since I first learned about it in 2002. Python is pretty limited in its support for functional programming, especially in its inability to do tail call optimization and its lack of suitable data structures.  &lt;/li&gt; &lt;li&gt;Learning about functional programming led me to Standard ML and its kin. Reading &lt;a href="http://www.cs.cmu.edu/~rwh/introsml/"&gt;Harper's Standard ML book&lt;/a&gt; profoundly affected how I think about programming, perhaps even more than did reading &lt;a href="http://mitpress.mit.edu/sicp/"&gt;SICP&lt;/a&gt;.  Much to my surprise, I found that static typing didn't have to be the nightmare I remembered from C programming. Indeed, typeful programming turns out to be a natural match for how I like to work, with types encoding—and enforcing—a great deal of the assumptions that go into function definition. Python, with its untyped variables, doesn't lend much support to this style. &lt;/li&gt; &lt;li&gt;Concurrency appears increasingly relevant. I'd like to learn more about concurrent programming, and apply it in practice. Specifically, message passing concurrency shows great promise, interacting well with a functional programming style and providing ready control over when nondeterminism appears (see, e.g., a &lt;a href="http://lambda-the-ultimate.org/node/3108"&gt;recent post on Lambda the Ultimate&lt;/a&gt;). Python isn't structured for the approach I want to explore. &lt;/li&gt;&lt;/ul&gt; Basically, there are things that I want to do that Python just gets in the way of, and the future of Python seems quite unlikely to include the changes I need. &lt;br /&gt;&lt;br /&gt;In light of the above, the landmark Python 3 release comes across as a good time to re-assess how I use Python. As a whole, I'm left thinking that I'd be better off directing more of my time to Scala, OCaml, or Haskell. The changes in Python 3 hardly seem worth even the relatively minor effort needed to upgrade at this point, so I think I'll just stick with the installation I have for now.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3164056879958238384-7481652884352693250?l=appliedabstraction.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://appliedabstraction.blogspot.com/feeds/7481652884352693250/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3164056879958238384&amp;postID=7481652884352693250' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/7481652884352693250'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/7481652884352693250'/><link rel='alternate' type='text/html' href='http://appliedabstraction.blogspot.com/2008/12/thoughts-on-python-30.html' title='Thoughts on Python 3.0'/><author><name>Michael Barber</name><uri>http://www.blogger.com/profile/15713879666980145844</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3164056879958238384.post-7808228062287077303</id><published>2008-11-18T12:44:00.001-08:00</published><updated>2008-11-18T12:47:05.822-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='polymorphism'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Passive-Aggressive Python</title><content type='html'>Consider &lt;code&gt;sum&lt;/code&gt;. It's great for adding up a list of numbers: &lt;pre&gt;Python 2.5.1 (r251:54863, Feb  4 2008, 21:48:13) &lt;br /&gt;[GCC 4.0.1 (Apple Inc. build 5465)] on darwin&lt;br /&gt;Type "help", "copyright", "credits" or "license" for more information.&lt;br /&gt;&gt;&gt;&gt; x = range(10); print x&lt;br /&gt;[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]&lt;br /&gt;&gt;&gt;&gt; sum(x)&lt;br /&gt;45&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Strings, on the other hand, it doesn't care for:&lt;pre&gt;&gt;&gt;&gt; y = list("abcdefghi"); print y&lt;br /&gt;['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i']&lt;br /&gt;&gt;&gt;&gt; sum(y, "")&lt;br /&gt;Traceback (most recent call last):&lt;br /&gt;  File "&lt;stdin&gt;", line 1, in &lt;module&gt;&lt;br /&gt;TypeError: sum() can't sum strings [use ''.join(seq) instead]&lt;br /&gt;&lt;/pre&gt; This is about performance. Repeatedly adding up strings behaves quite a bit differently than adding up numbers. Each addition causes a new string to be created, turning the whole thing into O(n&lt;sup&gt;2&lt;/sup&gt;), where n is the number of strings being concatenated, instead of the O(n) you would get by using &lt;code&gt;join&lt;/code&gt; or when &lt;code&gt;sum&lt;/code&gt;ming up numbers. &lt;br /&gt;&lt;br /&gt;Think that seems like a fair tradeoff? Think again: &lt;pre&gt;&gt;&gt;&gt; u = map(lambda a: [a], x); print u&lt;br /&gt;[[0], [1], [2], [3], [4], [5], [6], [7], [8], [9]]&lt;br /&gt;&gt;&gt;&gt; sum(u, [])&lt;br /&gt;[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]&lt;br /&gt;&gt;&gt;&gt; &lt;br /&gt;&lt;/pre&gt; and again: &lt;pre&gt;&gt;&gt;&gt; v = map(lambda a: (a,), x); print v&lt;br /&gt;[(0,), (1,), (2,), (3,), (4,), (5,), (6,), (7,), (8,), (9,)]&lt;br /&gt;&gt;&gt;&gt; sum(v, ())&lt;br /&gt;(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)&lt;br /&gt;&gt;&gt;&gt; &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Both of those have the same performance issues with &lt;code&gt;sum&lt;/code&gt; that strings have. &lt;br /&gt;&lt;br /&gt;So much for polymorphism. The interface is muddled, being neither specialized to numbers, nor polymorphic to objects that support addition. It's all quite silly, really, because there's no reason for &lt;code&gt;sum&lt;/code&gt; to behave like this. The worst-case time complexity is intended to be O(n), but easily becomes O(n&lt;sup&gt;2&lt;/sup&gt;) as shown above—and actually is entirely unpredictable because objects can make arbitrary calculations. On the other hand, the &lt;code&gt;TypeError&lt;/code&gt; raised for a string is an arbitrary restriction, since it could instead just internally make use of &lt;code&gt;''.join&lt;/code&gt;. &lt;br /&gt;&lt;br /&gt;Apparently, &lt;code&gt;sum&lt;/code&gt; is the way it is because that's just not how one should concatenate a string. Anything else that supports addition, go ahead, but for strings there is one—and only one—right way to do it. By fiat.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3164056879958238384-7808228062287077303?l=appliedabstraction.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://appliedabstraction.blogspot.com/feeds/7808228062287077303/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3164056879958238384&amp;postID=7808228062287077303' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/7808228062287077303'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/7808228062287077303'/><link rel='alternate' type='text/html' href='http://appliedabstraction.blogspot.com/2008/11/passive-aggressive-python.html' title='Passive-Aggressive Python'/><author><name>Michael Barber</name><uri>http://www.blogger.com/profile/15713879666980145844</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3164056879958238384.post-483640198939764317</id><published>2008-11-13T00:23:00.001-08:00</published><updated>2008-11-13T00:25:10.069-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='fun'/><title type='text'>Phrase from nearest book</title><content type='html'>Odd idea that I've seen first on &lt;a href="http://agiletesting.blogspot.com/2008/11/phrase-from-nearest-book-meme.html"&gt;Grig Gheorghiu's blog&lt;/a&gt;. Here are the rules:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Grab the nearest book.&lt;/li&gt; &lt;li&gt;Open it to page 56.&lt;/li&gt;&lt;li&gt;Find the fifth sentence.&lt;/li&gt; &lt;li&gt;Post the text of the sentence in your journal along with these instructions.&lt;/li&gt; &lt;li&gt;Dont dig for your favorite book, the cool book, or the intellectual one: pick the CLOSEST.&lt;/li&gt; &lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;For me, the nearest book was &lt;cite&gt;Practical Statistics&lt;/cite&gt; by Russell Langley, giving this: &lt;blockquote&gt;&lt;br /&gt;The average duration of numbness in these tests was 50% longer with Prilocaine than with Lignocaine, but the lack of any information about the degree of dispersion around these arithmetic means makes it impossible to assess the significance of the observed difference. &lt;br /&gt;&lt;/blockquote&gt;&lt;br /&gt;It may not be terribly poetic, but it is a quite profound idea in statistics.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3164056879958238384-483640198939764317?l=appliedabstraction.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://appliedabstraction.blogspot.com/feeds/483640198939764317/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3164056879958238384&amp;postID=483640198939764317' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/483640198939764317'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/483640198939764317'/><link rel='alternate' type='text/html' href='http://appliedabstraction.blogspot.com/2008/11/phrase-from-nearest-book.html' title='Phrase from nearest book'/><author><name>Michael Barber</name><uri>http://www.blogger.com/profile/15713879666980145844</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3164056879958238384.post-5195499539168356454</id><published>2008-10-15T10:03:00.001-07:00</published><updated>2008-12-21T01:49:52.419-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ICFP'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><category scheme='http://www.blogger.com/atom/ns#' term='LaTeX'/><title type='text'>Certainly More Interesting than Anything I've Ever Done with LaTeX</title><content type='html'>&lt;a href="http://sdh33b.blogspot.com/2008/07/icfp-contest-2008.html"&gt;That&lt;/a&gt; is just insane. But in a good way!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3164056879958238384-5195499539168356454?l=appliedabstraction.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://appliedabstraction.blogspot.com/feeds/5195499539168356454/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3164056879958238384&amp;postID=5195499539168356454' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/5195499539168356454'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/5195499539168356454'/><link rel='alternate' type='text/html' href='http://appliedabstraction.blogspot.com/2008/10/certainly-more-intteresting-than.html' title='Certainly More Interesting than Anything I&amp;#39;ve Ever Done with LaTeX'/><author><name>Michael Barber</name><uri>http://www.blogger.com/profile/15713879666980145844</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3164056879958238384.post-6511856888725485731</id><published>2008-09-16T13:42:00.001-07:00</published><updated>2008-09-16T13:42:47.498-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='humor'/><title type='text'>My Results for the Sarah Palin Baby Name Generator</title><content type='html'>The &lt;a href="http://politsk.blogspot.com/2008/09/sarah_13.html"&gt;Sarah Palin Baby Name Generator&lt;/a&gt; has been mentioned by many sites lately, so a description is probably not needed. My name would be &lt;em&gt;Thump Hummer Palin&lt;/em&gt;. &lt;br /&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3164056879958238384-6511856888725485731?l=appliedabstraction.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://appliedabstraction.blogspot.com/feeds/6511856888725485731/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3164056879958238384&amp;postID=6511856888725485731' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/6511856888725485731'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/6511856888725485731'/><link rel='alternate' type='text/html' href='http://appliedabstraction.blogspot.com/2008/09/my-results-for-sarah-palin-baby-name.html' title='My Results for the Sarah Palin Baby Name Generator'/><author><name>Michael Barber</name><uri>http://www.blogger.com/profile/15713879666980145844</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3164056879958238384.post-999934506039380855</id><published>2008-08-01T03:53:00.001-07:00</published><updated>2008-08-01T03:53:55.334-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Mac OS X'/><title type='text'>An Anniversary, of Sorts</title><content type='html'>It has been one year since the O'Reilly &lt;a href="http://www.macdevcenter.com/"&gt;MacDevCenter&lt;/a&gt; site has published an article on Cocoa. The articles since then are a mix of various Mac OS X tips and tricks, with a heavy emphasis on Aperture&amp;mdash;so no actual Mac development articles.&lt;br /&gt;&lt;br /&gt;It's been quite a while since O'Reilly has published a book on Mac development, too. There is the Missing Manual series, but it is really something distinct. Has O'Reilly given up on Mac developers?&lt;br /&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3164056879958238384-999934506039380855?l=appliedabstraction.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://appliedabstraction.blogspot.com/feeds/999934506039380855/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3164056879958238384&amp;postID=999934506039380855' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/999934506039380855'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/999934506039380855'/><link rel='alternate' type='text/html' href='http://appliedabstraction.blogspot.com/2008/08/anniversary-of-sorts.html' title='An Anniversary, of Sorts'/><author><name>Michael Barber</name><uri>http://www.blogger.com/profile/15713879666980145844</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3164056879958238384.post-5192001256830925905</id><published>2008-07-27T12:36:00.001-07:00</published><updated>2008-07-27T12:36:33.771-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='AppleScript'/><title type='text'>AppleScript Syntax: Dealing with the Depressing Reality</title><content type='html'>Given the preceding &lt;a href="http://appliedabstraction.blogspot.com/2008/07/applescript-syntax-depressing-corollary.html"&gt;two&lt;/a&gt; &lt;a href="http://appliedabstraction.blogspot.com/2008/07/applescript-syntax-some-depressing.html"&gt;posts&lt;/a&gt;, I hope it is clear that I think syntax is of too much concern in AppleScript discussions.  The preoccupation with syntax can hide real problems that might actually be sensibly discussed. It seems appropriate to mention some of the aspects of AppleScript that I think could be usefully discussed. I'll limit myself here to points where I can provide some practical tips as starting points. &lt;br /&gt;&lt;br /&gt;To begin, name resolution is pretty tricky. Rather than some sensible lexical scoping, there are complex rules. John Gruber provides an &lt;a href="http://daringfireball.net/2005/09/englishlikeness_monster"&gt;excellent description&lt;/a&gt; of how names are resolved, in the context of explaining a subtle bug. There is a fact that I've found useful for avoiding name conflicts: &lt;em&gt;you do not have use &lt;code&gt;tell&lt;/code&gt; blocks. &lt;/em&gt;&lt;br /&gt;&lt;br /&gt;Frequently, you just need to &lt;code&gt;tell&lt;/code&gt; applications to do a few things. However, example code often has the entire program in a single &lt;code&gt;tell&lt;/code&gt; block. That leads to all the tricky rules for determining when a term is looked up in an application dictionary, or a Scripting Addition, or as a handler in the script. Many of these issues can be eliminated by doing something like &lt;code&gt;tell application "Finder" to …&lt;/code&gt; instead of using the block format. In essence, this is the same idea as avoiding the use of an implicit &lt;code&gt;this&lt;/code&gt; or &lt;code&gt;self&lt;/code&gt; object, as seen in some object oriented languages.&lt;br /&gt;&lt;br /&gt;Said approach also is particularly enlightening on how much of your program is actually spent dealing with interprocess communication, and how much is program logic. Frequently, not much is actually spent on dealing with IPC, but it leads to a lot of complications. Simple solution: &lt;em&gt;encapsulate IPC in handlers.&lt;/em&gt; &lt;br /&gt;&lt;br /&gt;Text handling is important in many scripts. AppleScript is pretty weak in its text handling. Mac OS X comes with a powerful text processing system: the Unix shell. &lt;em&gt;Use &lt;code&gt;do shell script&lt;/code&gt; for text manipulation.&lt;/em&gt;&lt;br /&gt;&lt;br /&gt;AppleScript is lacking in the data structures it provides. The one I most frequently miss is an associative array, but it's not the only one. Many other languages provide a richer library of data structures, better support for defining your own, or both. You could often dispense entirely with AppleScript, except for communicating with applications, which might just be a few lines. &lt;em&gt;You can communicate with applications using the &lt;code&gt;osascript&lt;/code&gt; shell command.&lt;/em&gt; This lets you use just about any language you like, and still be able to gain the main benefit of AppleScript. &lt;br /&gt;&lt;br /&gt;There must be many more. &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3164056879958238384-5192001256830925905?l=appliedabstraction.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://appliedabstraction.blogspot.com/feeds/5192001256830925905/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3164056879958238384&amp;postID=5192001256830925905' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/5192001256830925905'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/5192001256830925905'/><link rel='alternate' type='text/html' href='http://appliedabstraction.blogspot.com/2008/07/applescript-syntax-dealing-with.html' title='AppleScript Syntax: Dealing with the Depressing Reality'/><author><name>Michael Barber</name><uri>http://www.blogger.com/profile/15713879666980145844</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3164056879958238384.post-4735333765903481560</id><published>2008-07-27T11:03:00.001-07:00</published><updated>2008-07-27T11:26:27.141-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='AppleScript'/><title type='text'>AppleScript Syntax: Some Depressing Examples</title><content type='html'>Let's expand on the &lt;a href="http://appliedabstraction.blogspot.com/2008/07/applescript-syntax-depressing-corollary.html"&gt;previous post&lt;/a&gt; by looking at some examples of how discussion of syntax poisons discussion of AppleScript. Just to be clear, the examples are taken from blogs I like and read regularly; I'm sure there are many other examples, but I haven't gone out of my way to find these. &lt;br /&gt;&lt;br /&gt;First, let's return to &lt;a href="http://daringfireball.net"&gt;Daring Fireball&lt;/a&gt;. John Gruber recently &lt;a href="http://daringfireball.net/linked/2008/06/03/english-likeness-monster"&gt;wrote&lt;/a&gt; that &lt;blockquote&gt; AppleScript, as a programming language, is a noble but failed experiment.&lt;/blockquote&gt; To support this, he links to an earlier article, &lt;a href="http://daringfireball.net/2005/09/englishlikeness_monster"&gt;&lt;cite&gt;The English-Likeness Monster,&lt;/cite&gt;&lt;/a&gt; in part of which he makes the far more modest claim that AppleScript's English-like syntax is a failed experiment. &lt;br /&gt;&lt;br /&gt;Well, which is it? Languages are more than their syntax. Reading the article doesn't clarify much. Gruber details how a bug he experienced was caused by a subtle name conflict in scripting dictionary terminology. He gives a cogent description of the semantics of name resolution in AppleScript, showing how the name resolution semantics leads to the bug he experienced. However, buried within is a lengthy rant on AppleScript's syntax. It looks like, and is identified as, a digression (well, it is called an "interpolation"—Gruber is a David Foster Wallace fan, as I recall), but gives the post its title. Just what is the intent? &lt;br /&gt;&lt;br /&gt;Additionally, there is mention of Python and JavaScript as having clearer, if more abstract, syntax than AppleScript, which helps to prevent such problems. However, if you had AppleScript's syntax but Python's name resolution, you literally &lt;em&gt;could not have the same error.&lt;/em&gt; There is a difference at a far deeper level than the syntax. To what extent are these articles supposed to be about syntax, semantics, surrounding tools, libraries (i.e., scripting additions),…? &lt;br /&gt;&lt;br /&gt;As a second example, let's take a look at an interesting article from Daniel Jalkut's &lt;a href="http://www.red-sweater.com/blog/"&gt;Red Sweater Blog&lt;/a&gt;, called &lt;a href="http://www.red-sweater.com/blog/502/apples-script"&gt;&lt;cite&gt;Apple's Script&lt;/cite&gt;&lt;/a&gt;. Jalkut makes what is essentially an economic argument that Apple should make JavaScript the default scripting language for the Mac, keeping AppleScript as an alternative point of access to the Open Scripting Architecture. His key point is that Apple is devoting significant resources to JavaScript, and will continue to do so for strategic reasons. Further, due to wide-spread experience with JavaScript, it is in practice easier for users, despite the supposed ease of use of AppleScript (aside: I dispute that AppleScript is easy to use). &lt;br /&gt;&lt;br /&gt;Nothing in the article depends in any way on the syntax of AppleScript, but look through the responses! Several people bring up AppleScript syntax, both as a positive and as a negative. Once the issue of syntax appears, the discussion pretty much stays there. It's a shame, because I think that Jalkut's point was an interesting one, and really does warrant some thought. &lt;br /&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3164056879958238384-4735333765903481560?l=appliedabstraction.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://appliedabstraction.blogspot.com/feeds/4735333765903481560/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3164056879958238384&amp;postID=4735333765903481560' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/4735333765903481560'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/4735333765903481560'/><link rel='alternate' type='text/html' href='http://appliedabstraction.blogspot.com/2008/07/applescript-syntax-some-depressing.html' title='AppleScript Syntax: Some Depressing Examples'/><author><name>Michael Barber</name><uri>http://www.blogger.com/profile/15713879666980145844</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3164056879958238384.post-775469664560006286</id><published>2008-07-27T10:08:00.001-07:00</published><updated>2009-09-10T08:20:21.416-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='AppleScript'/><title type='text'>AppleScript Syntax: the Depressing Corollary</title><content type='html'>John Gruber at Daring Fireball &lt;a href="http://daringfireball.net/linked/2008/07/21/cook-applescript"&gt;links to&lt;/a&gt; William Cook's &lt;a href="http://www.cs.utexas.edu/~wcook/Drafts/2006/ashopl.pdf"&gt;paper&lt;/a&gt; on the history of AppleScript. I've been meaning to write about the paper for a few weeks, based on a few recent blog posts relating to AppleScript. The history contains several facts that, in my opinion, are vital for understanding what AppleScript is today, and how we should approach it. In particular, there are some important points to be learned about AppleScript's syntax.&lt;br /&gt;&lt;br /&gt;I first read the paper a couple of years ago, after learning about it from &lt;a href="http://lambda-the-ultimate.org/node/1724"&gt;Lambda the Ultimate&lt;/a&gt;. William Cook is one of the original developers of AppleScript. He describes the development of the language for the third &lt;a href="http://research.ihost.com/hopl/HOPL-III.html"&gt;History of Programming Languages&lt;/a&gt; conference. The history of a programming language sound likely to be rather dull, but Cook's paper is far from dull. Cook sheds light on many aspects of AppleScript, making clear that the language is  a mix of successes and failures. What also becomes clear is that the designers were willing to try genuinely new ideas, not all of which worked out due to practical considerations. As Cook writes, &lt;blockquote&gt;AppleScript was developed by a small group with a short schedule, a tight budget and a big job. There was neither time nor money to fully research design choices.&lt;/blockquote&gt; It should not be surprising that some of those design choices were suboptimal or even failures.&lt;br /&gt;&lt;br /&gt;AppleScript's natural language syntax was one of those failures. Cook writes &lt;blockquote&gt;The experiment in designing a language that resembled natural languages (English and Japanese) was not successful. It was assume[d] that scripts should be presented in “natural language” so that average people could read and write them.… In the end the syntactic variations and ﬂexibility did more to confuse programmers than to help them out. The main problem is that AppleScript only appears to be a natural language. In fact[, it] is an artiﬁcial language, like any other programming language.… It is easy to read AppleScript, but quite hard to write it. &lt;/blockquote&gt; (I've corrected a few typos that were in the copy of the paper I have, which was an early draft.) Besides making AppleScript accessible to average people, there were additional goals for the natural language syntax. &lt;em&gt;None&lt;/em&gt; of them were successful.&lt;br /&gt;&lt;br /&gt;In his conclusion, Cook writes &lt;blockquote&gt;Many of the current problems in AppleScript can &lt;br /&gt;be traced to the use of syntax based on natural language…&lt;/blockquote&gt; Sadly, many critics of AppleScript would have that be the whole story. It is not, and acting as if it were hinders understanding of real problems with AppleScript, some of which could be addressed without changing the syntax at all. In fact, I will assert that the biggest problem with AppleScript's syntax is that it prevents meaningful discussion of AppleScript!&lt;br /&gt;&lt;br /&gt;I propose a variant of &lt;a href="http://en.wikipedia.org/wiki/Godwin's_law"&gt;Godwin's law&lt;/a&gt; for AppleScript: &lt;blockquote&gt; As an online discussion of AppleScript grows longer, the probability of the discussion devolving into a debate on the merits of AppleScript's syntax approaches one. At this point, nothing meaningful will be said, and the discussion is effectively over.&lt;/blockquote&gt; Too frequently, discussion of AppleScript actually begins on the topic of syntactic merits. This leads to the depressing corollary: &lt;blockquote&gt;Most discussions of AppleScript consist only of a debate on the syntactic merits of the language. There is nothing to be learned from these discussions.&lt;/blockquote&gt; Or, more simply: &lt;blockquote&gt;Most discussions of AppleScript contain nothing of value.&lt;/blockquote&gt; While harsh, I do feel these are an accurate description of most online (and, for that matter, offline) discussions of AppleScript that I've seen. &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3164056879958238384-775469664560006286?l=appliedabstraction.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://appliedabstraction.blogspot.com/feeds/775469664560006286/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3164056879958238384&amp;postID=775469664560006286' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/775469664560006286'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/775469664560006286'/><link rel='alternate' type='text/html' href='http://appliedabstraction.blogspot.com/2008/07/applescript-syntax-depressing-corollary.html' title='AppleScript Syntax: the Depressing Corollary'/><author><name>Michael Barber</name><uri>http://www.blogger.com/profile/15713879666980145844</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3164056879958238384.post-3536697400765196089</id><published>2008-06-25T08:56:00.001-07:00</published><updated>2008-06-25T08:56:57.184-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Google Apps'/><category scheme='http://www.blogger.com/atom/ns#' term='Gmail'/><title type='text'>A Small Tip on Catch All Address and Spam Blocking in Google Apps</title><content type='html'>I've recently signed up for &lt;a href="http://www.google.com/a/help/intl/en/var_1c.html"&gt;Google Apps&lt;/a&gt;, based largely on an &lt;a href="http://lifehacker.com/software/ask-lifehacker/what-does-google-apps-for-your-domain-actually-do-330318.php"&gt;article&lt;/a&gt; on &lt;a href="http://lifehacker.com/"&gt;Lifehacker&lt;/a&gt;. One interesting point was the idea of using a catch-all email address to block spam, as described in a &lt;a href="http://lifehacker.com/software/ask-lifehacker/what-does-google-apps-for-your-domain-actually-do-330318.php#c3224482"&gt;comment&lt;/a&gt; or with somewhat more detail in a different &lt;a href="http://lifehacker.com/369393/how-to-avoid-catch+all-domain-spam"&gt;article&lt;/a&gt;. Both explanations omit a relatively minor point that can lead to some frustration. &lt;br /&gt;&lt;br /&gt;To summarize the method, you just set up filters in a catch-all address to identify email that matches a simple pattern, and forward it to your own account. The result is that you can give out email address "aliases" for, e.g., web registration forms, without inviting spam to your real address. If you start getting spam to one of the aliases, you can block it directly with a filter. &lt;br /&gt;&lt;br /&gt;I set this up, and sent a test message to try it out. The message showed up in the catch-all account, but didn't get forwarded to my own account. The filter I used to set up the forward was identifying the messages correctly, but didn't send it on for some reason. &lt;br /&gt;&lt;br /&gt;After a fair amount of web searching, I found the answer. Email for Google Apps is &lt;a href="http://www.gmail.com"&gt;Gmail&lt;/a&gt;. Gmail does some modestly smart things to present a convenient interface; perhaps the best known is the way Gmail groups messages into "conversations." Something else it does is to hide messages that you've forwarded to yourself. This seems quite sensible in the context of conversations.&lt;br /&gt;&lt;br /&gt;It's probably clear what was happening, at this point. I sent some test messages from my own account, which was then forwarded back to me from the catch-all account. Gmail sees a messages forwarded to myself, and doesn't show it. I'm left scratching my head in puzzlement. &lt;br /&gt;&lt;br /&gt;After finding out about the hiding of self-forwards, I try sending a test message from a different account. It forwards without any trouble. Thus, for testing, you should always use a different account to send email than the account you want to receive it in.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3164056879958238384-3536697400765196089?l=appliedabstraction.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://appliedabstraction.blogspot.com/feeds/3536697400765196089/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3164056879958238384&amp;postID=3536697400765196089' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/3536697400765196089'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/3536697400765196089'/><link rel='alternate' type='text/html' href='http://appliedabstraction.blogspot.com/2008/06/small-tip-on-catch-all-address-and-spam.html' title='A Small Tip on Catch All Address and Spam Blocking in Google Apps'/><author><name>Michael Barber</name><uri>http://www.blogger.com/profile/15713879666980145844</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3164056879958238384.post-4864685873132088005</id><published>2008-06-07T09:23:00.001-07:00</published><updated>2008-06-07T11:46:20.160-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Mac OS X'/><category scheme='http://www.blogger.com/atom/ns#' term='Plaxo'/><title type='text'>Plaxo Synchronization Oddity </title><content type='html'>I've used &lt;a href="http://www.plaxo.com"&gt;Plaxo&lt;/a&gt; for a while to synchronize my address book across a couple of Macs and a web mail account or two. This was the original intent for Plaxo, and it has served me reasonably well as a replacement for the .Mac synchronization (which didn't serve me well, and was overpriced to boot). Today, I discovered a rather strange bit of behavior in the syncronization.&lt;br /&gt;&lt;br /&gt;Since I started using it, Plaxo has added a feature they call &lt;a href="http://pulse.plaxo.com/"&gt;Pulse&lt;/a&gt;. Pulse seems like an interesting variant on the social networking websites, focusing on allowing you to connect up feeds from the sites you actually use and centralize the social networking aspects (e.g., from &lt;a href="http://www.blogger.com"&gt;Blogger&lt;/a&gt;, &lt;a href="http://www.flickr.com/"&gt;Flickr&lt;/a&gt;, and &lt;a href="http://del.icio.us/"&gt;del.icio.us&lt;/a&gt;). I set up Pulse today. &lt;br /&gt;&lt;br /&gt;Without Pulse,  I never had much reason to take a look at the online version of my address book. With Pulse, I could see my own address plainly while setting up my profile. It was wrong.&lt;br /&gt;&lt;br /&gt;That was quite strange. The addresses at Plaxo came from synchronizing with the Address Book data on my Mac. Those were right! However, there was a chunk of my previous address showing up on Plaxo, mixed in with my current address. &lt;br /&gt;&lt;br /&gt;Here's what happened. I now live in Vienna. The address format for my Address Book card is thus set to Austrian. Before coming to Vienna, I lived in Portugal. The Portuguese format has a field for territorial subdivision, while the Austrian one doesn't use that. The extra stuff showing up in my current address is just the old territorial subdivision. Switching the display format for my Address Book card back to Portuguese shows the territorial subdivision field again. The old data is still there, just hidden with the Austrian formatting. It seems that Plaxo doesn't handle this situation correctly, treating all the data as still relevant (to be fair, I don't know if it is even possible for Plaxo to handle it right). &lt;br /&gt;&lt;br /&gt;Admittedly, the described case is a bit unusual. Regardless, it may affect others. The solution, fortunately, is simple: switch the Address Book card format back to the earlier country setting, delete the hidden, outdated fields, switch back to the correct country setting, and sync with Plaxo. &lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Update:&lt;/strong&gt; Or maybe Pulse doesn't let you connect a feed from Blogger. Mine doesn't seem to be working, anyway.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3164056879958238384-4864685873132088005?l=appliedabstraction.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://appliedabstraction.blogspot.com/feeds/4864685873132088005/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3164056879958238384&amp;postID=4864685873132088005' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/4864685873132088005'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/4864685873132088005'/><link rel='alternate' type='text/html' href='http://appliedabstraction.blogspot.com/2008/06/plaxo-synchronization-oddity.html' title='Plaxo Synchronization Oddity '/><author><name>Michael Barber</name><uri>http://www.blogger.com/profile/15713879666980145844</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3164056879958238384.post-8326295479272507729</id><published>2008-05-01T05:32:00.001-07:00</published><updated>2009-08-10T10:04:05.662-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='types'/><title type='text'>Tradeoffs for Type Inference</title><content type='html'>Here's a nice essay by Chris Smith: &lt;a href="http://cdsmith.twu.net/types.html"&gt;What to Know Before Debating Type Systems&lt;/a&gt;. Smith gives a nice introduction to some of the ideas and terminology for programming language type systems, aimed at the perennial static vs. dynamic debate. I quite like that he dismisses the strong vs weak typing distinction as meaningless. Some terminology that I'd have like to have seen is that of untyped languages and type safe languages, since so-called dynamic typing is just that: untyped, but type safe through dynamic checking. I suppose that would have caused a lot of, e.g., Python users to stop reading, which would be a shame.&lt;br /&gt;&lt;br /&gt;For the most part, I think Smith is right on with his presentation, although there are a few points where I'd quibble about details. However, I think that type inference warrants some further consideration. As Smith states, "Type inference is generally a big win." I fully agree with that, but there are some details worth making explicit. &lt;br /&gt;&lt;br /&gt;First, let's distinguish type inference from what we might call type propagation or local type inference. Type inference is figuring out the types of the expressions from the expressions themselves. Type inference is seen mostly in functional programming languages such as &lt;a href="http://www.standardml.org/"&gt;Standard ML&lt;/a&gt;, &lt;a href="http://caml.inria.fr/"&gt;Objective Caml&lt;/a&gt;, and &lt;a href="http://www.haskell.org"&gt;Haskell&lt;/a&gt;. For the most part, you don't need to write down the type signature for a function, you just define it and the compiler can work out the types. Despite this, it is common to give the type signatures for functions anyway, since the types provide a great deal of information. The type information is useful for understanding how to call and compose  functions, which is obviously important for functional programming. Further, declaring the type for a function can be a useful design aid, allowing a correctly typed function stub to be provided that will just raise an exception when called. In Standard ML, such a stub is defined like &lt;code&gt;fun stub x = raise Fail "Not implemented"&lt;/code&gt;. The inferred type of &lt;code&gt;stub&lt;/code&gt; is &lt;code&gt;'a -&gt; 'b&lt;/code&gt;, which is too general to be useful in any real function. By manually giving a type to &lt;code&gt;stub&lt;/code&gt;, you can use it to define other functions and the compiler will be able to do more useful checks. &lt;br /&gt;&lt;br /&gt;Type propagation is using known types to figure out the types of as many expressions as possible. The latter might take the form of declaring the types for functions and allowing the compiler to work out the types for all the expressions within the function bodies. If you would normally write out the types for functions anyway, you aren't really out much. You might have to write a few more type signatures, since you'd probably skip the signatures for little helper functions, but those types should be easy, anyway. There can also be gains from this approach, since you can use a more expressive type system for which type inference might not be decidable. Such an approach is taken in the &lt;a href="http://www.scala-lang.org/intro/inference.html"&gt;local type inference&lt;/a&gt; for &lt;a href="http://www.scala-lang.org/"&gt;Scala&lt;/a&gt;. There's no guarantee that types can be inferred, but they usually can be so you don't need to write type declarations for every variable. On the other hand, by softening the guarantees for inference, types can be used to do more. It can even fit in with untyped languages for optimization purposes; my understanding is that this was a big part of how Dylan compilers were able to produce &lt;a href="http://wiki.opendylan.org/wiki/view.dsp?title=What%20makes%20Dylan%20capable%20to%20product%20efficient%20code"&gt;fast executables&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;As a whole, having type inference provides not just benefits, but also places restrictions on the type system. Improving the type system may well make it worth giving up a general type inference algorithm in favor of weaker type propagation. The marginal cost of annotating some extra functions with types is just not a high price to pay. Turning it around, one really has the worst of both worlds with languages like C, C++, and Java: you have to declare the type of every variable, but it really doesn't provide you with any benefit. &lt;br /&gt;&lt;br /&gt;&lt;p&gt;(Via &lt;a href="http://lambda-the-ultimate.org/"&gt;Lambda the Ultimate&lt;/a&gt;.)&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3164056879958238384-8326295479272507729?l=appliedabstraction.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://appliedabstraction.blogspot.com/feeds/8326295479272507729/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3164056879958238384&amp;postID=8326295479272507729' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/8326295479272507729'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/8326295479272507729'/><link rel='alternate' type='text/html' href='http://appliedabstraction.blogspot.com/2008/05/tradeoffs-for-type-inference.html' title='Tradeoffs for Type Inference'/><author><name>Michael Barber</name><uri>http://www.blogger.com/profile/15713879666980145844</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3164056879958238384.post-2285122657087130903</id><published>2008-04-24T13:41:00.001-07:00</published><updated>2008-04-24T13:55:12.523-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='m4'/><category scheme='http://www.blogger.com/atom/ns#' term='AppleScript'/><category scheme='http://www.blogger.com/atom/ns#' term='SubEthaEdit'/><title type='text'>Code indentation in SubEthaEdit</title><content type='html'>There's a recent message on the &lt;a href="http://tech.groups.yahoo.com/group/SubEthaEdit/message/175"&gt;Yahoo! group for SubEthaEdit&lt;/a&gt; asking about automatic formatting of code in SubEthaEdit. By building on &lt;a href="http://appliedabstraction.blogspot.com/2008/01/seeing-latex-19-subethaedittools-or.html"&gt;SubEthaEditTools&lt;/a&gt;, it only takes about twenty minutes to connect an appropriate shell script to any given mode, calling out to, e.g., &lt;code&gt;indent&lt;/code&gt; for the C mode. But we can do better! &lt;br /&gt;&lt;br /&gt;It takes only a little more effort to install a general system for code reformatting into SubEthaEdit. First, we create a suitable AppleScript that calls out to a formatter:&lt;br /&gt;&lt;div style="text-align:left;color:#000000; background-color:#ffffff; border:solid black 1px; padding:0.5em 1em 0.5em 1em; overflow:auto;"&gt;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;property&lt;/strong&gt;&lt;/span&gt; prettyPrinter: &amp;quot;${PRETTYPRINTER}&amp;quot;&lt;br /&gt; &lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;if&lt;/strong&gt;&lt;/span&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;not&lt;/strong&gt;&lt;/span&gt; documentIsAvailable() &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;then&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;return&lt;/span&gt;&lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;end&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt; &lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;if&lt;/strong&gt;&lt;/span&gt; (modeSetting for &amp;quot;PRETTYPRINTER&amp;quot;) &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;is&lt;/strong&gt;&lt;/span&gt; missing value &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;then&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;display alert &amp;quot;Unable to re-indent.&amp;quot; message &amp;quot;You need to define PRETTYPRINTER for the mode.&amp;quot;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;return&lt;/span&gt;&lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;end&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt; &lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;if&lt;/strong&gt;&lt;/span&gt; selectionIsEmpty() &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;then&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;setDocumentText &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;to&lt;/strong&gt;&lt;/span&gt; (beautifulCode from documentText())&lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;else&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;extendSelection &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;with&lt;/strong&gt;&lt;/span&gt; extendingFront &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;and&lt;/strong&gt;&lt;/span&gt; extendingEnd&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;setSelectionText &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;to&lt;/strong&gt;&lt;/span&gt; (beautifulCode from selectionText())&lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;end&lt;/strong&gt;&lt;/span&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;if&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt; &lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;on&lt;/strong&gt;&lt;/span&gt; beautifulCode from sourceText&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;shellTransform &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;of&lt;/strong&gt;&lt;/span&gt; sourceText for modeEnvironment() through prettyPrinter without alteringLineEndings&lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;end&lt;/strong&gt;&lt;/span&gt; beautifulCode&lt;br /&gt; &lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;on&lt;/strong&gt;&lt;/span&gt; seescriptsettings()&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{displayName:&amp;quot;Re-Indent Lines&amp;quot;, shortDisplayName:&amp;quot;Re-Indent&amp;quot;, keyboardShortcut:&amp;quot;^@i&amp;quot;, toolbarIcon:&amp;quot;ToolbarIconRun&amp;quot;, inDefaultToolbar:&amp;quot;no&amp;quot;, toolbarTooltip:&amp;quot;Automatically re-indents lines&amp;quot;, inContextMenu:&amp;quot;yes&amp;quot;}&lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;end&lt;/strong&gt;&lt;/span&gt; seescriptsettings&lt;br /&gt; &lt;br /&gt; include(`SubEthaEditTools.applescript')&lt;/div&gt;&lt;br /&gt;The formatter is stored as a &lt;code&gt;PRETTYPRINTER&lt;/code&gt; environment variable. The &lt;code&gt;include&lt;/code&gt; at the very end is an &lt;code&gt;m4&lt;/code&gt; command, not AppleScript; if you don't know what it's for, you can either just cut and paste the contents of SubEthaEditTools into the script at that point, or read about it &lt;a href="http://appliedabstraction.blogspot.com/2008/01/what-role-should-script-editor-play-in.html"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;The logic of the AppleScript is pretty straightforward. I first check to make sure a document is open, then check to see if a formatter is defined for the mode through a &lt;code&gt;PRETTYPRINTER&lt;/code&gt; environment variable. I didn't do these sorts of checks in &lt;a href="http://appliedabstraction.blogspot.com/search/label/SEEing%20LaTeX"&gt;other scripts&lt;/a&gt; for SubEthaEdit, because those were always for just one mode, so I could assume that there was an open document and provide a sensible default for the mode. Here, we're going to put the script into the general scripts folder &lt;code&gt;~/Library/Application Support/SubEthaEdit/Scripts&lt;/code&gt;, so we can't make those assumptions anymore.&lt;br /&gt;&lt;br /&gt;At this point, I've made all the safety checks needed, so I can proceed to do the reformatting. If no text is selected, I pass all the text to the &lt;code&gt;beautifulCode&lt;/code&gt; handler, otherwise I extend the selection to full lines and pass the new contents to &lt;code&gt;beautifulCode&lt;/code&gt;. All that happens in &lt;code&gt;beautifulCode&lt;/code&gt; is that the text is handed off to the shell for formatting, with a suitable environment obtained from the mode. The results of &lt;code&gt;beautifulCode&lt;/code&gt; are then substituted for the original text. &lt;br /&gt;&lt;br /&gt;Finally, &lt;code&gt;seescriptsettings&lt;/code&gt; are defined. I set a keyboard shortcut of Command-Control-i; it was available, and allows a mnemonic of i for indent. I defined a toolbar item for it, with the run icon, which is pretty arbitrary; I didn't see anything better from the standard list. It can also be invoked within a document using the contextual menu.  Remember, this goes into the general scripts, so appears under the scripts menu, not the Mode menu. I saved mine as &lt;cite&gt;PrettyPrint.scpt&lt;/cite&gt;.&lt;br /&gt;&lt;br /&gt;One thing remains: how do we define the &lt;code&gt;PRETTYPRINTER&lt;/code&gt; for a mode? It's remarkably easy. The script I used for &lt;a href="http://appliedabstraction.blogspot.com/2007/10/seeing-latex-13-accessing-environment.html"&gt;setting the LaTeX mode environment&lt;/a&gt; doesn't actually depend on the LaTeX mode in any way, so it can be used directly by just putting it in the general scripts folder instead of within a mode. Actually, it is probably a good idea to also check that a document is open so that a mode can be identified. With &lt;a href="http://appliedabstraction.blogspot.com/2008/01/seeing-latex-19-subethaedittools-or.html"&gt;SubEthaEditTools&lt;/a&gt;, there's almost nothing to it:&lt;br /&gt;&lt;div style="text-align:left;color:#000000; background-color:#ffffff; border:solid black 1px; padding:0.5em 1em 0.5em 1em; overflow:auto;"&gt;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;if&lt;/strong&gt;&lt;/span&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;not&lt;/strong&gt;&lt;/span&gt; documentIsAvailable() &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;then&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;return&lt;/span&gt;&lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;end&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt; &lt;br /&gt; openEnvironmentSettings()&lt;br /&gt; &lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;on&lt;/strong&gt;&lt;/span&gt; seescriptsettings()&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;return&lt;/span&gt; {displayName:&amp;quot;Customize Mode...&amp;quot;, shortDisplayName:&amp;quot;Environment&amp;quot;, inContextMenu:&amp;quot;no&amp;quot;}&lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;end&lt;/strong&gt;&lt;/span&gt; seescriptsettings&lt;br /&gt; &lt;br /&gt; include(`SubEthaEditTools.applescript')&lt;br /&gt; &lt;/div&gt;&lt;br /&gt;This script, too, goes into the general scripts folder for SubEthaEdit; I named mine &lt;cite&gt;OpenEnvironment.scpt&lt;/cite&gt;. Although this second script is quite simple, it has a very profound effect, adding a preferences system for  &lt;em&gt;all&lt;/em&gt; the SubEthaEdit modes. I wonder what else it could be used for?&lt;br /&gt;&lt;br /&gt;And that's it. Install the two scripts, define some appropriate &lt;code&gt;PRETTYPRINTER&lt;/code&gt; values (being sure to quote the shell command appropriately!), and reformat away. For your convenience, you can download compiled scripts for the &lt;a href="http://www.box.net/shared/vejrsky04k"&gt;formatter&lt;/a&gt; and the &lt;a href="http://www.box.net/shared/ebfodi5eso"&gt;mode environment settings&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3164056879958238384-2285122657087130903?l=appliedabstraction.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://appliedabstraction.blogspot.com/feeds/2285122657087130903/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3164056879958238384&amp;postID=2285122657087130903' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/2285122657087130903'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/2285122657087130903'/><link rel='alternate' type='text/html' href='http://appliedabstraction.blogspot.com/2008/04/code-indentation-in-subethaedit.html' title='Code indentation in SubEthaEdit'/><author><name>Michael Barber</name><uri>http://www.blogger.com/profile/15713879666980145844</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3164056879958238384.post-3287857154712644929</id><published>2008-04-19T01:14:00.001-07:00</published><updated>2008-04-19T01:15:54.492-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='atheism'/><category scheme='http://www.blogger.com/atom/ns#' term='animation'/><category scheme='http://www.blogger.com/atom/ns#' term='music'/><title type='text'>Very Slick Video</title><content type='html'>I'm not a big fan of Rush, but this video is quite fun, bordering on stunning.&lt;br /&gt;&lt;p&gt;&lt;a href="http://bobbysbrane.com/animation-fun/animations/MaLnAr-BobbysBrane.mov"&gt;Watch it.&lt;/a&gt; &lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;(Via &lt;a href="http://scienceblogs.com/pharyngula/"&gt;Pharyngula&lt;/a&gt;.)&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3164056879958238384-3287857154712644929?l=appliedabstraction.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://appliedabstraction.blogspot.com/feeds/3287857154712644929/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3164056879958238384&amp;postID=3287857154712644929' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/3287857154712644929'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/3287857154712644929'/><link rel='alternate' type='text/html' href='http://appliedabstraction.blogspot.com/2008/04/very-slick-video.html' title='Very Slick Video'/><author><name>Michael Barber</name><uri>http://www.blogger.com/profile/15713879666980145844</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3164056879958238384.post-8004206244305260513</id><published>2008-04-12T11:49:00.001-07:00</published><updated>2008-04-12T13:38:15.552-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='SEEing LaTeX'/><category scheme='http://www.blogger.com/atom/ns#' term='AppleScript'/><category scheme='http://www.blogger.com/atom/ns#' term='SubEthaEdit'/><category scheme='http://www.blogger.com/atom/ns#' term='shell'/><category scheme='http://www.blogger.com/atom/ns#' term='LaTeX'/><title type='text'>SEEing LaTeX 29: Listening to LaTeX</title><content type='html'>The &lt;a href="http://codingmonkeys.de/subethaedit"&gt;SubEthaEdit&lt;/a&gt; mode I've developed in this series of posts is generally rather quiet. It doesn't announce successful production of a PDF from a LaTeX file, it just creates it and opens it. It also doesn't tell you when an error occurs. &lt;br /&gt;&lt;br /&gt;You can have the errors reported by piping the output from the call to &lt;code&gt;pdflatex&lt;/code&gt; into some command line tool. Exactly what tool to use is less clear. The natural choice for SubEthaEdit would be the &lt;code&gt;see&lt;/code&gt; command line too, but it's not very convenient, and has some real &lt;a href="http://appliedabstraction.blogspot.com/2008/03/seeing-latex-28-some-notes-on-command.html"&gt;problems&lt;/a&gt;. Beyond that, calling out to &lt;code&gt;see&lt;/code&gt; repeatedly will produce a new window each time, which is just annoying.&lt;br /&gt;&lt;br /&gt;The &lt;a href="http://www.python.org/"&gt;Python&lt;/a&gt; mode has a nicer behavior. It has a &lt;cite&gt;Check Syntax&lt;/cite&gt; command that always writes to a window named &lt;cite&gt;Python Check Syntax&lt;/cite&gt;, opening it if need be or overwriting the contents otherwise. The  &lt;a href="http://www.lua.org/"&gt;Lua&lt;/a&gt; mode has an analogous &lt;cite&gt;Check Syntax&lt;/cite&gt; command with similar behavior.&lt;br /&gt;&lt;br /&gt;Let's make a shell script that does something similar. First, I encapsulate the relevant bits of AppleScript from the Python and Lua modes into some simple handlers. Second, I embed those into a shell script using the &lt;a href="http://appliedabstraction.blogspot.com/2008/04/bit-more-on-applescript-and-stdin.html"&gt;approach&lt;/a&gt; recently discussed here. The resulting script I call &lt;code&gt;seeless&lt;/code&gt;, as it is usable as something like the &lt;code&gt;less&lt;/code&gt; pager. I'll defer the text of the script until the end, first describing the usage.&lt;br /&gt;&lt;br /&gt;Basic usage is just to pipe in some text:&lt;br /&gt;&lt;code&gt;echo Hello World! | seeless&lt;/code&gt;&lt;br /&gt;This writes the text &lt;cite&gt;Hello World&lt;/cite&gt; into a SubEthaEdit window titled &lt;cite&gt;seeless message&lt;/cite&gt;, opening a new window if necessary or replacing the text in an existing window. Multiple windows with the same title are a bit problematic, with no guarantee that the window you want will be the one written to. Don't do that.&lt;br /&gt;&lt;br /&gt;The title of the window can be specified:&lt;br /&gt;&lt;code&gt;echo Hello World! | seeless -t"A message for you, direct from the shell"&lt;/code&gt;&lt;br /&gt;Appropriate choice of title allows a SubEthaEdit mode to establish its own reporting window. &lt;br /&gt;&lt;br /&gt;The window is normally brought to the front when it is written to. Call &lt;code&gt;seeless&lt;/code&gt; as&lt;br /&gt;&lt;code&gt;echo Hello World! | seeless -b&lt;/code&gt;&lt;br /&gt;to leave the window in the background. This allows, e.g., a reporting window to be kept out of the way as a tab and only checked when something seems to be wrong.&lt;br /&gt;&lt;br /&gt;There are two different modes, insert and append, for writing to the window. For insert mode, the window is cleared before writing the text from &lt;code&gt;stdin&lt;/code&gt;, while append mode just appends to any existing text. To set append more, use:&lt;br /&gt;&lt;code&gt;echo Hello World! | seeless -a&lt;/code&gt;&lt;br /&gt;Insert mode is the default, but a flag exists for it, too:&lt;br /&gt;&lt;code&gt;echo Hello World! | seeless -i&lt;/code&gt;&lt;br /&gt;Multiple flags for the insert and append mode can be given, with the final one determining the behavior.&lt;br /&gt;&lt;br /&gt;In append mode, the text from a second call to &lt;code&gt;seeless&lt;/code&gt; follows immediately after the text from the first. To give some visual space, specify a separator:&lt;br /&gt;&lt;code&gt;echo Hello World! | seeless -s"----------"&lt;/code&gt;&lt;br /&gt;When a separator is given with the &lt;code&gt;-s&lt;/code&gt; flag, append mode is automatically set. It is also possible to use a separator in insert mode, giving a form of header. For example,&lt;br /&gt;&lt;code&gt;echo Hello World! | seeless -s"$(date)" -i&lt;/code&gt;&lt;br /&gt;shows the time when &lt;code&gt;seeless&lt;/code&gt; was called. Another possibility would be to have a cluster of related programs writing to the same window, and using the separator to specify the program that wrote the latest text.&lt;br /&gt;&lt;br /&gt;Let's take a look at how I put those options into use with the LaTeX mode. I set &lt;code&gt;SEE_BIBTEX&lt;/code&gt; to: &lt;div style="text-align:left;color:#000000; background-color:#ffffff; border:solid black 1px; padding:0.5em 1em 0.5em 1em; overflow:auto;font-size:small; font-family:monospace; "&gt;&lt;span style="color:#760f15;"&gt;'bibtex &amp;quot;&lt;/span&gt;&lt;span style="color:#c4620a;"&gt;${FILE%.tex}&lt;/span&gt;&lt;span style="color:#760f15;"&gt;&amp;quot; | &amp;quot;&lt;/span&gt;&lt;span style="color:#c4620a;"&gt;$HOME&lt;/span&gt;&lt;span style="color:#760f15;"&gt;/Library/bin/seeless&amp;quot; -t&amp;quot;LaTeX Messages&amp;quot; -s&amp;quot;bibtex ran at $(date)\n&amp;quot; -b -i &amp;amp;&amp;gt; /dev/null &amp;amp;'&lt;/span&gt;&lt;/div&gt; &lt;code&gt;SEE_LATEX_CLEANUP&lt;/code&gt; to: &lt;div style="text-align:left;color:#000000; background-color:#ffffff; border:solid black 1px; padding:0.5em 1em 0.5em 1em; overflow:auto;font-size:small; font-family:monospace; "&gt;&lt;span style="color:#760f15;"&gt;'latexmk -C &amp;quot;&lt;/span&gt;&lt;span style="color:#c4620a;"&gt;$FILE&lt;/span&gt;&lt;span style="color:#760f15;"&gt;&amp;quot; | &amp;quot;&lt;/span&gt;&lt;span style="color:#c4620a;"&gt;$HOME&lt;/span&gt;&lt;span style="color:#760f15;"&gt;/Library/bin/seeless&amp;quot; -t&amp;quot;LaTeX Messages&amp;quot; -s&amp;quot;latexmk -C ran at $(date)\n&amp;quot; -b -i &amp;amp;&amp;gt; /dev/null &amp;amp;'&lt;/span&gt;&lt;/div&gt; and &lt;code&gt;SEE_LATEX_COMPILER&lt;/code&gt; to: &lt;div style="text-align:left;color:#000000; background-color:#ffffff; border:solid black 1px; padding:0.5em 1em 0.5em 1em; overflow:auto;font-size:small; font-family:monospace; "&gt;&lt;span style="color:#760f15;"&gt;'latexmk -pdf -quiet &amp;quot;&lt;/span&gt;&lt;span style="color:#c4620a;"&gt;$FILE&lt;/span&gt;&lt;span style="color:#760f15;"&gt;&amp;quot; | &amp;quot;&lt;/span&gt;&lt;span style="color:#c4620a;"&gt;$HOME&lt;/span&gt;&lt;span style="color:#760f15;"&gt;/Library/bin/seeless&amp;quot; -t&amp;quot;LaTeX Messages&amp;quot; -s&amp;quot;latexmk ran at $(date)\n&amp;quot; -b -i &amp;amp;&amp;gt; /dev/null &amp;amp;'&lt;/span&gt;&lt;/div&gt; Note that I have redirected the output of each call to &lt;code&gt;seeless&lt;/code&gt; to &lt;code&gt;/dev/null&lt;/code&gt; and made the calls asynchronous with &lt;code&gt;&amp;&lt;/code&gt;—SubEthaEdit hangs without doing this, requiring a force quit.&lt;br /&gt;&lt;br /&gt;With the above settings, the LaTeX mode will cause SubEthaEdit to open up a report window titled &lt;cite&gt;LaTeX Messages&lt;/cite&gt;  whenever a document is typeset,  &lt;code&gt;bibtex&lt;/code&gt; is run, or the auxiliary files are cleaned up. I can put it out of the way, either as a tab or background document; because I've used the &lt;code&gt;-b&lt;/code&gt; flag for each call, the report window will stay out of the way until I want it brought to the front. I've used a separator to show which feature was most recently used and at what time it was called. I've set insert mode with an &lt;code&gt;-i&lt;/code&gt; flag, so I only see the latest call; by eliminating this flag, I'd have a chronological record of all the calls made (in the current editing session, anyway).&lt;br /&gt;&lt;br /&gt;I haven't been using this very long, so there may be some bugs. However, it seems quite solid, and is definitely useful already. Download it &lt;a href="http://www.box.net/shared/pspw089sk8"&gt;here&lt;/a&gt;.    &lt;br /&gt;&lt;br /&gt;As promised above, I'll give the text of the script here, too. I've formatted the script as a shell script, to better show how the shell variables are used to adapt the behavior of the embedded AppleScript. &lt;div style="text-align:left;color:#000000; background-color:#ffffff; border:solid black 1px; padding:0.5em 1em 0.5em 1em; overflow:auto;font-size:small; font-family:monospace; "&gt;&lt;span style="color:#236e25;"&gt;&lt;em&gt;#!/bin/sh&lt;br /&gt; &lt;/em&gt;&lt;/span&gt;&lt;br /&gt; &lt;span style="color:#236e25;"&gt;&lt;em&gt;# Writes stdin to a SubEthaEdit document, modifying the contents if the&lt;br /&gt; # document already exists. Document is selected by title, with a default&lt;br /&gt; # title of 'seeless'. Title can be specified with a '-t' flag. If there &lt;br /&gt; # are multiple documents with the given title, one is chosen arbitrarily.&lt;br /&gt; #&lt;br /&gt; # Writing to the document occurs in two modes, insert and append. With&lt;br /&gt; # insert mode, the document is cleared before any text is written to &lt;br /&gt; # it. In append mode, any existing text is maintained, with the new&lt;br /&gt; # text appended at the end. By giving an '-a' flag, append mode is set.&lt;br /&gt; # Giving an '-i' flag sets insert mode; insert mode is the default.&lt;br /&gt; #&lt;br /&gt; # A separator can be specified. The separator is written to the &lt;br /&gt; # document before the text from stdin. Specifying a separator also sets &lt;br /&gt; # append mode (but this can be turned off again if desired with an '-i' &lt;br /&gt; # flag). The separator is specified with an '-s' flag; the argument &lt;br /&gt; # following the flag is used as the separator.&lt;br /&gt; # &lt;br /&gt; # Giving an '-h' flag shows a usage summary. Any other flags are ignored.&lt;br /&gt; #&lt;br /&gt; &lt;/em&gt;&lt;/span&gt;&lt;br /&gt; &lt;span style="color:#236e25;"&gt;&lt;em&gt;#$Id: seeless.sh,v 1.9 2008/04/12 20:33:56 mjb Exp $&lt;br /&gt; &lt;/em&gt;&lt;/span&gt;&lt;br /&gt; &lt;span style="color:#236e25;"&gt;&lt;em&gt;# Copyright (c) 2008, Michael J. Barber&lt;br /&gt; # &lt;br /&gt; # Permission is hereby granted, free of charge, to any person obtaining&lt;br /&gt; # a copy of this software and associated documentation files (the&lt;br /&gt; # &amp;quot;Software&amp;quot;), to deal in the Software without restriction, including&lt;br /&gt; # without limitation the rights to use, copy, modify, merge, publish,&lt;br /&gt; # distribute, sublicense, and/or sell copies of the Software, and to&lt;br /&gt; # permit persons to whom the Software is furnished to do so, subject&lt;br /&gt; # to the following conditions:&lt;br /&gt; # &lt;br /&gt; # The above copyright notice and this permission notice shall be&lt;br /&gt; # included in all copies or substantial portions of the Software.&lt;br /&gt; # &lt;br /&gt; # THE SOFTWARE IS PROVIDED &amp;quot;AS IS&amp;quot;, WITHOUT WARRANTY OF ANY KIND,&lt;br /&gt; # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF&lt;br /&gt; # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.&lt;br /&gt; # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR&lt;br /&gt; # ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF&lt;br /&gt; # CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION&lt;br /&gt; # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.&lt;br /&gt; &lt;/em&gt;&lt;/span&gt;&lt;br /&gt; &lt;br /&gt; PROGRAM=$(&lt;span style="color:#880088;"&gt;basename&lt;/span&gt; &lt;span style="color:#c4620a;"&gt;$0&lt;/span&gt;)&lt;br /&gt; &lt;br /&gt; &lt;span style="color:#003369;"&gt;usage&lt;/span&gt;()&lt;br /&gt; {&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#880088;"&gt;echo&lt;/span&gt; &lt;span style="color:#760f15;"&gt;&amp;quot;Usage: &lt;/span&gt;&lt;span style="color:#c4620a;"&gt;$PROGRAM&lt;/span&gt;&lt;span style="color:#760f15;"&gt; [-ahi] [-t title] [-s separator]&amp;quot;&lt;/span&gt;&lt;br /&gt; }&lt;br /&gt; &lt;br /&gt; title=&lt;span style="color:#760f15;"&gt;&amp;quot;&lt;/span&gt;&lt;span style="color:#c4620a;"&gt;$PROGRAM&lt;/span&gt;&lt;span style="color:#760f15;"&gt; message&amp;quot;&lt;/span&gt;&lt;br /&gt; shouldClear=&lt;span style="color:#880088;"&gt;true&lt;/span&gt;&lt;br /&gt; shouldSeparate=&lt;span style="color:#880088;"&gt;false&lt;/span&gt;&lt;br /&gt; shouldBringToFront=&lt;span style="color:#880088;"&gt;true&lt;/span&gt;&lt;br /&gt; separator=&lt;span style="color:#760f15;"&gt;&amp;quot;----------&amp;quot;&lt;/span&gt;&lt;br /&gt; &lt;span style="color:#881350;"&gt;while&lt;/span&gt; &lt;span style="color:#440088;"&gt;getopts&lt;/span&gt; :t:ias:bh opt&lt;br /&gt; &lt;span style="color:#881350;"&gt;do&lt;/span&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#881350;"&gt;case&lt;/span&gt; &lt;span style="color:#c4620a;"&gt;$opt&lt;/span&gt; &lt;span style="color:#881350;"&gt;in&lt;/span&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;t) &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;title=&lt;span style="color:#760f15;"&gt;&amp;quot;&lt;/span&gt;&lt;span style="color:#c4620a;"&gt;$OPTARG&lt;/span&gt;&lt;span style="color:#760f15;"&gt;&amp;quot;&lt;/span&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;;;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;i) &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;shouldClear=&lt;span style="color:#880088;"&gt;true&lt;/span&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;;;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;a) &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;shouldClear=&lt;span style="color:#880088;"&gt;false&lt;/span&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;;;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;s) &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;separator=&lt;span style="color:#760f15;"&gt;&amp;quot;&lt;/span&gt;&lt;span style="color:#c4620a;"&gt;$OPTARG&lt;/span&gt;&lt;span style="color:#760f15;"&gt;&amp;quot;&lt;/span&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;shouldSeparate=&lt;span style="color:#880088;"&gt;true&lt;/span&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;shouldClear=&lt;span style="color:#880088;"&gt;false&lt;/span&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;;;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;b) &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;shouldBringToFront=&lt;span style="color:#880088;"&gt;false&lt;/span&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;;;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;h) &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;usage&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#440088;"&gt;exit&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;0&lt;/span&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;;;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#760f15;"&gt;'?'&lt;/span&gt;) &amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#880088;"&gt;echo&lt;/span&gt; &lt;span style="color:#760f15;"&gt;&amp;quot;&lt;/span&gt;&lt;span style="color:#c4620a;"&gt;$PROGRAM&lt;/span&gt;&lt;span style="color:#760f15;"&gt;: invalid option -&lt;/span&gt;&lt;span style="color:#c4620a;"&gt;$OPTARG&lt;/span&gt;&lt;span style="color:#760f15;"&gt;&amp;quot;&lt;/span&gt; &amp;gt;&amp;amp;&lt;span style="color:#0000ff;"&gt;2&lt;/span&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;usage &amp;gt;&amp;amp;&lt;span style="color:#0000ff;"&gt;2&lt;/span&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#440088;"&gt;exit&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;1&lt;/span&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;;;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#881350;"&gt;esac&lt;/span&gt;&lt;br /&gt; &lt;span style="color:#881350;"&gt;done&lt;/span&gt;&lt;br /&gt; &lt;br /&gt; &lt;span style="color:#440088;"&gt;shift&lt;/span&gt; $((OPTIND - &lt;span style="color:#0000ff;"&gt;1&lt;/span&gt;))&lt;br /&gt; &lt;br /&gt; /usr/bin/osascript &amp;gt; /dev/null &amp;lt;&amp;lt;ASCPT&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#440088;"&gt;set&lt;/span&gt; newContents to &lt;span style="color:#760f15;"&gt;&amp;quot;$(cat | sed -e 's/\\/\\\\/g' -e 's/\&amp;quot;/\\\&amp;quot;/g')&amp;quot;&lt;/span&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#440088;"&gt;set&lt;/span&gt; seeDoc &lt;span style="color:#003369;"&gt;to &lt;/span&gt;(ensureSEEDocumentExists &lt;span style="color:#881350;"&gt;for&lt;/span&gt; &lt;span style="color:#760f15;"&gt;&amp;quot;&lt;/span&gt;&lt;span style="color:#c4620a;"&gt;$title&lt;/span&gt;&lt;span style="color:#760f15;"&gt;&amp;quot;&lt;/span&gt;)&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#881350;"&gt;if&lt;/span&gt; &lt;span style="color:#c4620a;"&gt;$shouldClear&lt;/span&gt; &lt;span style="color:#881350;"&gt;then&lt;/span&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;replaceContents &lt;span style="color:#881350;"&gt;for&lt;/span&gt; the seeDoc by &lt;span style="color:#760f15;"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;end &lt;span style="color:#881350;"&gt;if&lt;/span&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#881350;"&gt;if&lt;/span&gt; &lt;span style="color:#c4620a;"&gt;$shouldSeparate&lt;/span&gt; &lt;span style="color:#881350;"&gt;then&lt;/span&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;extendContents &lt;span style="color:#881350;"&gt;for&lt;/span&gt; the seeDoc by the &lt;span style="color:#760f15;"&gt;&amp;quot;&lt;/span&gt;&lt;span style="color:#c4620a;"&gt;$separator&lt;/span&gt;&lt;span style="color:#760f15;"&gt;&amp;quot;&lt;/span&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;end &lt;span style="color:#881350;"&gt;if&lt;/span&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;extendContents &lt;span style="color:#881350;"&gt;for&lt;/span&gt; the seeDoc by the newContents&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#881350;"&gt;if&lt;/span&gt; &lt;span style="color:#c4620a;"&gt;$shouldBringToFront&lt;/span&gt; &lt;span style="color:#881350;"&gt;then&lt;/span&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;tell application &lt;span style="color:#760f15;"&gt;&amp;quot;SubEthaEdit&amp;quot;&lt;/span&gt; to show the seeDoc&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;end &lt;span style="color:#881350;"&gt;if&lt;/span&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;to ensureSEEDocumentExists &lt;span style="color:#881350;"&gt;for&lt;/span&gt; doctitle&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;tell application &lt;span style="color:#760f15;"&gt;&amp;quot;SubEthaEdit&amp;quot;&lt;/span&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#881350;"&gt;if&lt;/span&gt; exists document named doctitle &lt;span style="color:#881350;"&gt;then&lt;/span&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;document named doctitle&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#881350;"&gt;else&lt;/span&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#880088;"&gt;make&lt;/span&gt; new document with properties {name:doctitle}&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;end &lt;span style="color:#881350;"&gt;if&lt;/span&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;end tell&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;end ensureSEEDocumentExists&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;to replaceContents &lt;span style="color:#881350;"&gt;for&lt;/span&gt; seeDoc by newContents&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;tell application &lt;span style="color:#760f15;"&gt;&amp;quot;SubEthaEdit&amp;quot;&lt;/span&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#440088;"&gt;set&lt;/span&gt; the contents of seeDoc to newContents&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#880088;"&gt;clear&lt;/span&gt; change marks of seeDoc&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;try&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#440088;"&gt;set&lt;/span&gt; modified of seeDoc to &lt;span style="color:#880088;"&gt;false&lt;/span&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;end try&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;end tell&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;end replaceContents&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;to extendContents &lt;span style="color:#881350;"&gt;for&lt;/span&gt; seeDoc by moreContents&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;tell application &lt;span style="color:#760f15;"&gt;&amp;quot;SubEthaEdit&amp;quot;&lt;/span&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#881350;"&gt;if&lt;/span&gt; &lt;span style="color:#760f15;"&gt;&amp;quot;&amp;quot;&lt;/span&gt; is not equal to the contents of the &lt;span style="color:#880088;"&gt;last&lt;/span&gt; paragraph of seeDoc &lt;span style="color:#881350;"&gt;then&lt;/span&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#440088;"&gt;set&lt;/span&gt; the contents of the &lt;span style="color:#880088;"&gt;last&lt;/span&gt; insertion point of the &lt;span style="color:#880088;"&gt;last&lt;/span&gt; paragraph of seeDoc to &lt;span style="color:#440088;"&gt;return&lt;/span&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;end &lt;span style="color:#881350;"&gt;if&lt;/span&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#440088;"&gt;set&lt;/span&gt; the contents of the &lt;span style="color:#880088;"&gt;last&lt;/span&gt; insertion point of the &lt;span style="color:#880088;"&gt;last&lt;/span&gt; paragraph of seeDoc to moreContents&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#880088;"&gt;clear&lt;/span&gt; change marks of seeDoc&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;try&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#440088;"&gt;set&lt;/span&gt; modified of seeDoc to &lt;span style="color:#880088;"&gt;false&lt;/span&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;end try&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;end tell&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;end extendContents&lt;br /&gt; ASCPT&lt;br /&gt; &lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3164056879958238384-8004206244305260513?l=appliedabstraction.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://appliedabstraction.blogspot.com/feeds/8004206244305260513/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3164056879958238384&amp;postID=8004206244305260513' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/8004206244305260513'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/8004206244305260513'/><link rel='alternate' type='text/html' href='http://appliedabstraction.blogspot.com/2008/04/seeing-latex-29-listening-to-latex.html' title='SEEing LaTeX 29: Listening to LaTeX'/><author><name>Michael Barber</name><uri>http://www.blogger.com/profile/15713879666980145844</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3164056879958238384.post-3752615665630491261</id><published>2008-04-12T08:48:00.001-07:00</published><updated>2008-04-12T13:40:12.640-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='SEEing LaTeX'/><category scheme='http://www.blogger.com/atom/ns#' term='AppleScript'/><category scheme='http://www.blogger.com/atom/ns#' term='shell'/><title type='text'>A Bit More on AppleScript and stdin</title><content type='html'>I can get even simpler than the &lt;a href="http://appliedabstraction.blogspot.com/2008/04/applescript-shell-and-stdin.html"&gt;earlier script&lt;/a&gt; to pass &lt;code&gt;stdin&lt;/code&gt; to an AppleScript embedding within a shell script.  Consider this:&lt;div style="text-align:left;color:#000000; background-color:#ffffff; border:solid black 1px; padding:0.5em 1em 0.5em 1em; overflow:auto;font-size:small; font-family:monospace; "&gt;&lt;span style="color:#236e25;"&gt;&lt;em&gt;#!/bin/sh&lt;br /&gt; &lt;/em&gt;&lt;/span&gt;&lt;br /&gt; /usr/bin/osascript &amp;gt; /dev/null &amp;lt;&amp;lt;ASCPT&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#440088;"&gt;set&lt;/span&gt; stdinText to &lt;span style="color:#760f15;"&gt;&amp;quot;$(cat | sed -e 's/\\/\\\\/g' -e 's/\&amp;quot;/\\\&amp;quot;/g')&amp;quot;&lt;/span&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;tell application &lt;span style="color:#760f15;"&gt;&amp;quot;TextEdit&amp;quot;&lt;/span&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;activate&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#880088;"&gt;make&lt;/span&gt; new document with properties {text:stdinText}&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;end tell&lt;br /&gt; ASCPT&lt;br /&gt; &lt;/div&gt; No need for the temporary file anymore, just use &lt;code&gt;cat&lt;/code&gt; from within the AppleScript portion. The result of &lt;code&gt;cat&lt;/code&gt; needs to be piped through &lt;code&gt;sed&lt;/code&gt; in order to prevent problems with quoting, with a bunch of backslashes to get the special characters right. This may need further tweaking.&lt;br /&gt;&lt;br /&gt;I ran into this when I tried using the earlier script for showing the results of the various scripts for the SubEthaEdit &lt;a href="http://appliedabstraction.blogspot.com/search/label/SEEing%20LaTeX"&gt;LaTeX mode&lt;/a&gt;. The earlier script failed when used for showing the results of &lt;a href="http://appliedabstraction.blogspot.com/2008/01/seeing-latex-24-for-completeness-bibtex.html"&gt;&lt;code&gt;bibtex&lt;/code&gt;&lt;/a&gt;. For some reason that I've not been able to work out, the AppleScript Standard Additions are no longer reached and the call to &lt;code&gt;do shell script&lt;/code&gt; fails. The new approach works, so far at least.&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3164056879958238384-3752615665630491261?l=appliedabstraction.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://appliedabstraction.blogspot.com/feeds/3752615665630491261/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3164056879958238384&amp;postID=3752615665630491261' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/3752615665630491261'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/3752615665630491261'/><link rel='alternate' type='text/html' href='http://appliedabstraction.blogspot.com/2008/04/bit-more-on-applescript-and-stdin.html' title='A Bit More on AppleScript and stdin'/><author><name>Michael Barber</name><uri>http://www.blogger.com/profile/15713879666980145844</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3164056879958238384.post-6793161658982350463</id><published>2008-04-10T11:00:00.001-07:00</published><updated>2009-07-03T23:48:37.080-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='AppleScript'/><category scheme='http://www.blogger.com/atom/ns#' term='shell'/><title type='text'>AppleScript, Shell, and stdin</title><content type='html'>AppleScript can be called from shell scripts, effectively giving access to Mac OS X applications from the underlying Unix tools. The AppleScript is embedded into the shell script as a here document, and invoked using &lt;code&gt;osascript&lt;/code&gt;. I've given an &lt;a href="http://appliedabstraction.blogspot.com/2007/07/seeing-latex-3-from-pdfview-to.html"&gt;example&lt;/a&gt; of this approach before.&lt;br /&gt;&lt;br /&gt;However, there is something that I've never really been clear on. How does the AppleScript portion of the shell script read from &lt;code&gt;stdin&lt;/code&gt;? Standard I/O is fundamental to Unix programming, so it is essential that AppleScript be able to access  &lt;code&gt;stdin&lt;/code&gt;. Searching with Google hasn't enlightened me. There seems to be no useful parallel with &lt;code&gt;stdout&lt;/code&gt;, which works as you'd hope, with &lt;code&gt;osascript&lt;/code&gt; writing transparently to it.&lt;br /&gt;&lt;br /&gt;After a bit of thinking, I came up with this:&lt;br /&gt;&lt;div style="text-align:left;color:#000000; background-color:#ffffff; border:solid black 1px; padding:0.5em 1em 0.5em 1em; overflow:auto;font-size:small; font-family:monospace; "&gt;&lt;span style="color:#236e25;"&gt;&lt;em&gt;#!/bin/sh&lt;br /&gt; &lt;/em&gt;&lt;/span&gt;&lt;br /&gt; STANDIN=$(&lt;span style="color:#880088;"&gt;mktemp&lt;/span&gt; /tmp/seereport.XXXXXXXXXXXX) || &lt;span style="color:#440088;"&gt;exit&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;1&lt;/span&gt;&lt;br /&gt; &lt;span style="color:#880088;"&gt;cat&lt;/span&gt; &amp;gt; &lt;span style="color:#c4620a;"&gt;$STANDIN&lt;/span&gt;&lt;br /&gt; &lt;br /&gt; /usr/bin/osascript &amp;gt; /dev/null &amp;lt;&amp;lt;ASCPT&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#440088;"&gt;set&lt;/span&gt; stdinText to &lt;span style="color:#881350;"&gt;do&lt;/span&gt; shell script &lt;span style="color:#760f15;"&gt;&amp;quot;cat &lt;/span&gt;&lt;span style="color:#c4620a;"&gt;$STANDIN&lt;/span&gt;&lt;span style="color:#760f15;"&gt;&amp;quot;&lt;/span&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;tell application &lt;span style="color:#760f15;"&gt;&amp;quot;TextEdit&amp;quot;&lt;/span&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;activate&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#880088;"&gt;make&lt;/span&gt; new document with properties {text:stdinText}&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;end tell&lt;br /&gt; ASCPT&lt;br /&gt; &lt;br /&gt; &lt;span style="color:#440088;"&gt;trap&lt;/span&gt; &lt;span style="color:#760f15;"&gt;'rm -f &lt;/span&gt;&lt;span style="color:#c4620a;"&gt;$STANDIN&lt;/span&gt;&lt;span style="color:#760f15;"&gt;'&lt;/span&gt; EXIT&lt;br /&gt; &lt;br /&gt; &lt;/div&gt; &lt;br /&gt;I write &lt;code&gt;stdin&lt;/code&gt; to a temporary file using &lt;code&gt;cat&lt;/code&gt;, then read it back out in the AppleScript portion, again using &lt;code&gt;cat&lt;/code&gt;. As an example, I just open a new TextEdit document with the text from &lt;code&gt;stdin&lt;/code&gt; as its contents. &lt;br /&gt;&lt;br /&gt;Saving this as &lt;code&gt;minimal.sh&lt;/code&gt;, I can then create a new TextEdit document from the shell with: &lt;br /&gt;&lt;div style="text-align:left;color:#000000; background-color:#ffffff; border:solid black 1px; padding:0.5em 1em 0.5em 1em; overflow:auto;font-size:small; font-family:monospace; "&gt;&lt;span style="color:#880088;"&gt;echo&lt;/span&gt; hello world | ./minimal.&lt;span style="color:#880088;"&gt;sh&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;This works, so I've managed to get at &lt;code&gt;stdin&lt;/code&gt;. It seems pretty roundabout though. Is there a better or recommended approach?&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Update:&lt;/strong&gt; I suppose it is worth mentioning that one could use &lt;code&gt;pbcopy&lt;/code&gt; and &lt;code&gt;pbpaste&lt;/code&gt; to avoid using the temporary file. However, doing so modifies the clipboard, so I prefer the approach shown. &lt;br /&gt;&lt;br /&gt;Further, it would be possible to read the temp file using AppleScript commands, instead of calling &lt;code&gt;cat&lt;/code&gt; with &lt;code&gt;do shell script&lt;/code&gt;. That's too fiddly for a minimal example. Beyond that, I don't see much point to it, since I'd do any processing in the shell, just using AppleScript to pass the text to an application. I can't think of any applications where a stream approach would buy us anything. &lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Update:&lt;/strong&gt; Modified example of using &lt;code&gt;minimal.sh&lt;/code&gt; to actually use  &lt;code&gt;minimal.sh&lt;/code&gt;!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3164056879958238384-6793161658982350463?l=appliedabstraction.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://appliedabstraction.blogspot.com/feeds/6793161658982350463/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3164056879958238384&amp;postID=6793161658982350463' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/6793161658982350463'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/6793161658982350463'/><link rel='alternate' type='text/html' href='http://appliedabstraction.blogspot.com/2008/04/applescript-shell-and-stdin.html' title='AppleScript, Shell, and stdin'/><author><name>Michael Barber</name><uri>http://www.blogger.com/profile/15713879666980145844</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3164056879958238384.post-6438737100891696243</id><published>2008-03-23T12:17:00.001-07:00</published><updated>2008-04-12T08:48:08.239-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Skim'/><category scheme='http://www.blogger.com/atom/ns#' term='pdfsync'/><category scheme='http://www.blogger.com/atom/ns#' term='SEEing LaTeX'/><category scheme='http://www.blogger.com/atom/ns#' term='SubEthaEdit'/><category scheme='http://www.blogger.com/atom/ns#' term='shell'/><category scheme='http://www.blogger.com/atom/ns#' term='Mac OS X'/><title type='text'>SEEing LaTeX 28: Some Critical Notes on the 'see' Command Line Tool</title><content type='html'>I've &lt;a href="http://appliedabstraction.blogspot.com/2007/07/seeing-latex-3-from-pdfview-to.html"&gt;used&lt;/a&gt; the &lt;code&gt;see&lt;/code&gt; command line tool to relate a LaTeX previewer to &lt;a href="https://www.codingmonkeys.de/subethaedit/"&gt;SubEthaEdit&lt;/a&gt; using &lt;a href="http://itexmac.sourceforge.net/pdfsync.html"&gt;pdfsync&lt;/a&gt;. Unfortunately, I've become aware that the natural usage of &lt;code&gt;see&lt;/code&gt; has a definite problem. Plugging in &lt;code&gt;see&lt;/code&gt; and &lt;code&gt;-g %line "%file"&lt;/code&gt; for the "PDFSync support" preferences in &lt;a href="http://skim-app.sourceforge.net/"&gt;Skim&lt;/a&gt; doesn't work quite the way one would hope.&lt;br /&gt;&lt;br /&gt;With that usage, which I'd consider to be the most natural, you wind up producing a new &lt;code&gt;see&lt;/code&gt; process each time you command-click in Skim to switch over to SubEthaEdit. That process, due to shortcomings in the design of &lt;code&gt;see&lt;/code&gt;, will hang around until you close the document in SubEthaEdit. Of course, if you're using SubEthaEdit and Skim together like that, you're in the middle of editing a LaTeX document, so you're not likely to be closing the document very promptly. Do that enough, and you could consume all the user processes allowed by Mac OS X. If you've not experience running out of user processes, let's just describe it as Not Fun.&lt;br /&gt;&lt;br /&gt;I don't think it is likely to be a big problem, especially with the more friendly process limits in Mac OS X Leopard, but it is definitely a real problem. The problem gets compounded with each new usage of &lt;code&gt;see&lt;/code&gt; for integrating external applications or reporting from a mode script. End result is that some caution is warranted when using &lt;code&gt;see&lt;/code&gt;, and it should be omitted in favor of another approach, if necessary. Personally, I'm still using it.&lt;br /&gt;&lt;br /&gt;I've requested an enhancement to &lt;code&gt;see&lt;/code&gt; consisting of a 'quiet' mode, like seen in, e.g., &lt;code&gt;grep&lt;/code&gt;. That would eliminate the issue, and, as a side benefit, considerably simplify using &lt;code&gt;see&lt;/code&gt; from AppleScripts for SubEthaEdit modes. Let's hope the Coding Monkeys act on it.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3164056879958238384-6438737100891696243?l=appliedabstraction.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://appliedabstraction.blogspot.com/feeds/6438737100891696243/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3164056879958238384&amp;postID=6438737100891696243' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/6438737100891696243'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/6438737100891696243'/><link rel='alternate' type='text/html' href='http://appliedabstraction.blogspot.com/2008/03/seeing-latex-28-some-notes-on-command.html' title='SEEing LaTeX 28: Some Critical Notes on the &amp;#39;see&amp;#39; Command Line Tool'/><author><name>Michael Barber</name><uri>http://www.blogger.com/profile/15713879666980145844</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3164056879958238384.post-596881286055610964</id><published>2008-02-17T09:12:00.001-08:00</published><updated>2009-08-10T10:00:31.511-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='SEEing LaTeX'/><title type='text'>SEEing LaTeX 27: End in Sight</title><content type='html'>The activity here today, after a few weeks of silence, reflects that I've finished the documentation. I've spent some time earlier today getting everything ready. Finally, I am very pleased to announce that you can &lt;a href="http://www.box.net/shared/7xkmovugw8"&gt;download&lt;/a&gt; the mode.&lt;br /&gt;&lt;br /&gt;What comes next? That depends on you! Download it, try it out, and let me know about any problems, either by posting a comment or by sending me some email. &lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Update:&lt;/strong&gt; The scripts developed in &lt;a href="http://appliedabstraction.blogspot.com/search/label/SEEing%20LaTeX"&gt;this series&lt;/a&gt; have been incorporated into the LaTeX mode distributed with &lt;a href="http://www.codingmonkeys.de/subethaedit/"&gt;SubEthaEdit&lt;/a&gt;. It should not normally be necessary to download the mode from the link given here, but I'll leave the link active in case anyone sees a need for my version.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Update 2:&lt;/strong&gt; With SubEthaEdit 3.5, the LaTeX mode linked here is out of date, not supporting folding. You should not use it without good reason, as the mode that comes with SEE has everying this version does and more. I will leave the mode available for now, but I don't see much use for it. &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3164056879958238384-596881286055610964?l=appliedabstraction.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://appliedabstraction.blogspot.com/feeds/596881286055610964/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3164056879958238384&amp;postID=596881286055610964' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/596881286055610964'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/596881286055610964'/><link rel='alternate' type='text/html' href='http://appliedabstraction.blogspot.com/2008/02/seeing-latex-27-end-in-sight.html' title='SEEing LaTeX 27: End in Sight'/><author><name>Michael Barber</name><uri>http://www.blogger.com/profile/15713879666980145844</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3164056879958238384.post-7749845520672924089</id><published>2008-02-17T06:12:00.001-08:00</published><updated>2008-02-17T06:12:02.154-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='SEEing LaTeX'/><title type='text'>SEEing LaTeX 26: Sample Environment Settings</title><content type='html'>As a starting point for customizing the LaTeX mode, here are some sample environment settings:&lt;br /&gt;&lt;br /&gt;&lt;div style="text-align:left;color:#000000; background-color:#ffffff; border:solid black 1px; padding:0.5em 1em 0.5em 1em; overflow:auto;font-size:small; font-family:monospace; "&gt;&lt;span style="color:#236e25;"&gt;&lt;em&gt;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot;?&amp;gt;&lt;/em&gt;&lt;/span&gt;&lt;br /&gt; &lt;span style="color:#236e25;"&gt;&lt;em&gt;&amp;lt;!DOCTYPE plist PUBLIC &amp;quot;-//Apple//DTD PLIST 1.0//EN&amp;quot; &amp;quot;http://www.apple.com/DTDs/PropertyList-1.0.dtd&amp;quot;&amp;gt;&lt;/em&gt;&lt;/span&gt;&lt;br /&gt; &lt;span style="color:#881280;"&gt;&lt;em&gt;&amp;lt;plist &lt;/em&gt;&lt;/span&gt;&lt;span style="color:#994500;"&gt;version&lt;/span&gt;&lt;span style="color:#881280;"&gt;&lt;em&gt;=&lt;/em&gt;&lt;/span&gt;&lt;span style="color:#1a1aa6;"&gt;&amp;quot;1.0&amp;quot;&lt;/span&gt;&lt;span style="color:#881280;"&gt;&lt;em&gt;&amp;gt;&lt;/em&gt;&lt;/span&gt;&lt;br /&gt; &lt;span style="color:#881280;"&gt;&lt;em&gt;&amp;lt;dict&amp;gt;&lt;/em&gt;&lt;/span&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#881280;"&gt;&lt;em&gt;&amp;lt;key&amp;gt;&lt;/em&gt;&lt;/span&gt;BIBINPUTS&lt;span style="color:#881280;"&gt;&lt;em&gt;&amp;lt;/key&amp;gt;&lt;/em&gt;&lt;/span&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#881280;"&gt;&lt;em&gt;&amp;lt;string&amp;gt;&lt;/em&gt;&lt;/span&gt;&amp;quot;$HOME/Library/texmf/bibtex/bib&amp;quot;&lt;span style="color:#881280;"&gt;&lt;em&gt;&amp;lt;/string&amp;gt;&lt;/em&gt;&lt;/span&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#881280;"&gt;&lt;em&gt;&amp;lt;key&amp;gt;&lt;/em&gt;&lt;/span&gt;SEE_BIBTEX&lt;span style="color:#881280;"&gt;&lt;em&gt;&amp;lt;/key&amp;gt;&lt;/em&gt;&lt;/span&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#881280;"&gt;&lt;em&gt;&amp;lt;string&amp;gt;&lt;/em&gt;&lt;/span&gt;'bibtex &amp;quot;${FILE%.tex}&amp;quot; | open -f -a SubEthaEdit'&lt;span style="color:#881280;"&gt;&lt;em&gt;&amp;lt;/string&amp;gt;&lt;/em&gt;&lt;/span&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#881280;"&gt;&lt;em&gt;&amp;lt;key&amp;gt;&lt;/em&gt;&lt;/span&gt;SEE_LATEX_CLEANUP&lt;span style="color:#881280;"&gt;&lt;em&gt;&amp;lt;/key&amp;gt;&lt;/em&gt;&lt;/span&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#881280;"&gt;&lt;em&gt;&amp;lt;string&amp;gt;&lt;/em&gt;&lt;/span&gt;'latexmk -C &amp;quot;$FILE&amp;quot;'&lt;span style="color:#881280;"&gt;&lt;em&gt;&amp;lt;/string&amp;gt;&lt;/em&gt;&lt;/span&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#881280;"&gt;&lt;em&gt;&amp;lt;key&amp;gt;&lt;/em&gt;&lt;/span&gt;SEE_LATEX_COMMENT&lt;span style="color:#881280;"&gt;&lt;em&gt;&amp;lt;/key&amp;gt;&lt;/em&gt;&lt;/span&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#881280;"&gt;&lt;em&gt;&amp;lt;string&amp;gt;&lt;/em&gt;&lt;/span&gt;'&amp;quot;$SEE_MODE_RESOURCES&amp;quot;/bin/comment.sh &amp;quot;% &amp;quot;'&lt;span style="color:#881280;"&gt;&lt;em&gt;&amp;lt;/string&amp;gt;&lt;/em&gt;&lt;/span&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#881280;"&gt;&lt;em&gt;&amp;lt;key&amp;gt;&lt;/em&gt;&lt;/span&gt;SEE_LATEX_COMPILER&lt;span style="color:#881280;"&gt;&lt;em&gt;&amp;lt;/key&amp;gt;&lt;/em&gt;&lt;/span&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#881280;"&gt;&lt;em&gt;&amp;lt;string&amp;gt;&lt;/em&gt;&lt;/span&gt;'latexmk -pdf -quiet &amp;quot;$FILE&amp;quot;'&lt;span style="color:#881280;"&gt;&lt;em&gt;&amp;lt;/string&amp;gt;&lt;/em&gt;&lt;/span&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#881280;"&gt;&lt;em&gt;&amp;lt;key&amp;gt;&lt;/em&gt;&lt;/span&gt;SEE_LATEX_COMPILEVIEWER&lt;span style="color:#881280;"&gt;&lt;em&gt;&amp;lt;/key&amp;gt;&lt;/em&gt;&lt;/span&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#881280;"&gt;&lt;em&gt;&amp;lt;string&amp;gt;&lt;/em&gt;&lt;/span&gt;'open -a Skim &amp;quot;$PRODUCT&amp;quot;'&lt;span style="color:#881280;"&gt;&lt;em&gt;&amp;lt;/string&amp;gt;&lt;/em&gt;&lt;/span&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#881280;"&gt;&lt;em&gt;&amp;lt;key&amp;gt;&lt;/em&gt;&lt;/span&gt;SEE_LATEX_PRODUCT_TYPE&lt;span style="color:#881280;"&gt;&lt;em&gt;&amp;lt;/key&amp;gt;&lt;/em&gt;&lt;/span&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#881280;"&gt;&lt;em&gt;&amp;lt;string&amp;gt;&lt;/em&gt;&lt;/span&gt;pdf&lt;span style="color:#881280;"&gt;&lt;em&gt;&amp;lt;/string&amp;gt;&lt;/em&gt;&lt;/span&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#881280;"&gt;&lt;em&gt;&amp;lt;key&amp;gt;&lt;/em&gt;&lt;/span&gt;SEE_LATEX_VIEWER&lt;span style="color:#881280;"&gt;&lt;em&gt;&amp;lt;/key&amp;gt;&lt;/em&gt;&lt;/span&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#881280;"&gt;&lt;em&gt;&amp;lt;string&amp;gt;&lt;/em&gt;&lt;/span&gt;'export __CF_USER_TEXT_ENCODING=0x1F5:0:0; /Applications/Skim.app/Contents/SharedSupport/displayline $LINE &amp;quot;$PRODUCT&amp;quot;'&lt;span style="color:#881280;"&gt;&lt;em&gt;&amp;lt;/string&amp;gt;&lt;/em&gt;&lt;/span&gt;&lt;br /&gt; &lt;span style="color:#881280;"&gt;&lt;em&gt;&amp;lt;/dict&amp;gt;&lt;/em&gt;&lt;/span&gt;&lt;br /&gt; &lt;span style="color:#881280;"&gt;&lt;em&gt;&amp;lt;/plist&amp;gt;&lt;/em&gt;&lt;/span&gt;&lt;br /&gt; &lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3164056879958238384-7749845520672924089?l=appliedabstraction.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://appliedabstraction.blogspot.com/feeds/7749845520672924089/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3164056879958238384&amp;postID=7749845520672924089' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/7749845520672924089'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/7749845520672924089'/><link rel='alternate' type='text/html' href='http://appliedabstraction.blogspot.com/2008/02/seeing-latex-26-sample-environment.html' title='SEEing LaTeX 26: Sample Environment Settings'/><author><name>Michael Barber</name><uri>http://www.blogger.com/profile/15713879666980145844</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3164056879958238384.post-6985498302571227876</id><published>2008-01-07T13:02:00.001-08:00</published><updated>2008-02-17T06:16:11.398-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='SEEing LaTeX'/><category scheme='http://www.blogger.com/atom/ns#' term='SubEthaEdit'/><category scheme='http://www.blogger.com/atom/ns#' term='LaTeX'/><title type='text'>SEEing LaTeX 25: Putting Things in Order</title><content type='html'>As noted &lt;a href="http://appliedabstraction.blogspot.com/2008/01/seeing-latex-23-am-i-done.html"&gt;below&lt;/a&gt;, the entries in the LaTeX mode menu for &lt;a href="http://codingmonkeys.de/subethaedit"&gt;SubEthaEdit&lt;/a&gt; need to be put into a sensible order. After some thinking, I decided that there are really three groups of scripts: scripts for interacting with the LaTeX system, scripts for simplifying typing, and a script for interacting with the LaTeX mode itself. &lt;br /&gt;&lt;br /&gt;Ideally, we'd put the three into groups divided by a horizontal rule. Unfortunately, SEE does not (yet) allow dividing lines to be inserted into the menu. Regardless, let's organize the scripts into the three groups, and - with one exception- just alphabetize within the groups. The exception is for the "Typeset and View" menu item; it strikes me as natural to put that first in the list.&lt;br /&gt;&lt;br /&gt;End result is a menu ordered as:&lt;br /&gt;&lt;br /&gt;Typeset and View&lt;br /&gt;Clean Up Auxiliary Files&lt;br /&gt;Run BibTeX&lt;br /&gt;View&lt;br /&gt;&lt;br /&gt;Complete Citation&lt;br /&gt;Inline Math&lt;br /&gt;Insert Environment...&lt;br /&gt;Un/Comment Selected Lines&lt;br /&gt;&lt;br /&gt;Customize Mode...&lt;br /&gt;&lt;br /&gt;Although I've broken the groups apart with spacing, they'll just run together in the menu. Perhaps a later version of SEE will allow some more structure to be added. &lt;br /&gt;&lt;br /&gt;One final change is that I've renamed the "Mode Environment..." menu item to "Customize Mode...". The two distinct meanings of "environment" struck me as confusing, and "Insert Environment..." is simply too appropriate for LaTeX to change.&lt;br /&gt;&lt;br /&gt;I'll add another entry to the menu as soon as possible. It will go into the third group, with a title something like "Mode Help". I still have to write the mode help, first.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3164056879958238384-6985498302571227876?l=appliedabstraction.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://appliedabstraction.blogspot.com/feeds/6985498302571227876/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3164056879958238384&amp;postID=6985498302571227876' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/6985498302571227876'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/6985498302571227876'/><link rel='alternate' type='text/html' href='http://appliedabstraction.blogspot.com/2008/01/seeing-latex-25-putting-things-in-order.html' title='SEEing LaTeX 25: Putting Things in Order'/><author><name>Michael Barber</name><uri>http://www.blogger.com/profile/15713879666980145844</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3164056879958238384.post-8160518165666100775</id><published>2008-01-06T12:06:00.001-08:00</published><updated>2008-02-17T06:18:53.579-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='BibTeX'/><category scheme='http://www.blogger.com/atom/ns#' term='SEEing LaTeX'/><category scheme='http://www.blogger.com/atom/ns#' term='AppleScript'/><category scheme='http://www.blogger.com/atom/ns#' term='SubEthaEdit'/><category scheme='http://www.blogger.com/atom/ns#' term='shell'/><category scheme='http://www.blogger.com/atom/ns#' term='LaTeX'/><title type='text'>SEEing LaTeX 24: For Completeness, BibTeX</title><content type='html'>I guess the &lt;a href="http://appliedabstraction.blogspot.com/2008/01/seeing-latex-23-am-i-done.html"&gt;right solution&lt;/a&gt; is to add a menu item for running &lt;code&gt;bibtex&lt;/code&gt;, and that's it. Since I don't use &lt;code&gt;makeindex&lt;/code&gt; myself, I'll leave that aside, unless someone wants to contribute scripts or just examples of use. I'd guess that the scripts would be easy enough, just adapt the ones I'll present for BibTeX. &lt;br /&gt;&lt;br /&gt;Here's the AppleScript:&lt;br /&gt;&lt;div style="text-align:left;color:#000000; background-color:#ffffff; border:solid black 1px; padding:0.5em 1em 0.5em 1em; overflow:auto;"&gt;checkSaveStatus without updating&lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;set&lt;/strong&gt;&lt;/span&gt; bibScript &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;to&lt;/strong&gt;&lt;/span&gt; join &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;of&lt;/strong&gt;&lt;/span&gt; {modeEnvironment(), quotedForm for &amp;quot;$SEE_MODE_RESOURCES/bin/runbibtex.sh&amp;quot;, quotedForm for documentPath()} by &lt;span style="color:#0b0bff;"&gt;space&lt;/span&gt;&lt;br /&gt; do shell script bibScript&lt;br /&gt; &lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;on&lt;/strong&gt;&lt;/span&gt; seescriptsettings()&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;return&lt;/span&gt; {displayName:&amp;quot;Run BibTeX&amp;quot;}&lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;end&lt;/strong&gt;&lt;/span&gt; seescriptsettings&lt;br /&gt; &lt;br /&gt; include(`SubEthaEditTools.applescript')&lt;br /&gt; &lt;/div&gt;&lt;br /&gt;&lt;br /&gt;And here's the shell script that it calls:&lt;br /&gt;&lt;div style="text-align:left;color:#000000; background-color:#ffffff; border:solid black 1px; padding:0.5em 1em 0.5em 1em; overflow:auto;font-size:small; font-family:monospace; "&gt;&lt;span style="color:#236e25;"&gt;&lt;em&gt;#!/bin/sh&lt;br /&gt; &lt;/em&gt;&lt;/span&gt;&lt;br /&gt; &lt;span style="color:#236e25;"&gt;&lt;em&gt;#$Id: runbibtex.sh,v 1.1 2008/01/06 19:09:49 mjb Exp $&lt;br /&gt; &lt;/em&gt;&lt;/span&gt;&lt;br /&gt; PATH=&lt;span style="color:#760f15;"&gt;&amp;quot;&lt;/span&gt;&lt;span style="color:#c4620a;"&gt;$PATH&lt;/span&gt;&lt;span style="color:#760f15;"&gt;:/usr/texbin:/usr/local/bin&amp;quot;&lt;/span&gt;&lt;br /&gt; &lt;span style="color:#881350;"&gt;export&lt;/span&gt; PATH&lt;br /&gt; &lt;br /&gt; BIBTEX=${SEE_BIBTEX:-&lt;span style="color:#760f15;"&gt;'bibtex &amp;quot;$(basename &lt;/span&gt;&lt;span style="color:#c4620a;"&gt;$FILE&lt;/span&gt;&lt;span style="color:#760f15;"&gt; .tex)&amp;quot;'&lt;/span&gt;}&lt;br /&gt; FILE=&lt;span style="color:#760f15;"&gt;&amp;quot;$(basename &amp;quot;&lt;/span&gt;&lt;span style="color:#c4620a;"&gt;$1&lt;/span&gt;&lt;span style="color:#760f15;"&gt;&amp;quot;)&amp;quot;&lt;/span&gt;&lt;br /&gt; DIRNAME=&lt;span style="color:#760f15;"&gt;&amp;quot;$(dirname &amp;quot;&lt;/span&gt;&lt;span style="color:#c4620a;"&gt;$1&lt;/span&gt;&lt;span style="color:#760f15;"&gt;&amp;quot;)&amp;quot;&lt;/span&gt;&lt;br /&gt; &lt;br /&gt; &lt;span style="color:#440088;"&gt;cd&lt;/span&gt; &lt;span style="color:#760f15;"&gt;&amp;quot;&lt;/span&gt;&lt;span style="color:#c4620a;"&gt;$DIRNAME&lt;/span&gt;&lt;span style="color:#760f15;"&gt;&amp;quot;&lt;/span&gt;&lt;br /&gt; &lt;span style="color:#440088;"&gt;eval&lt;/span&gt; &lt;span style="color:#c4620a;"&gt;$BIBTEX&lt;/span&gt; &lt;br /&gt; &lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3164056879958238384-8160518165666100775?l=appliedabstraction.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://appliedabstraction.blogspot.com/feeds/8160518165666100775/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3164056879958238384&amp;postID=8160518165666100775' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/8160518165666100775'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/8160518165666100775'/><link rel='alternate' type='text/html' href='http://appliedabstraction.blogspot.com/2008/01/seeing-latex-24-for-completeness-bibtex.html' title='SEEing LaTeX 24: For Completeness, BibTeX'/><author><name>Michael Barber</name><uri>http://www.blogger.com/profile/15713879666980145844</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3164056879958238384.post-4376200904559517535</id><published>2008-01-06T09:40:00.001-08:00</published><updated>2008-01-06T09:41:35.907-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='SEEing LaTeX'/><category scheme='http://www.blogger.com/atom/ns#' term='SubEthaEdit'/><category scheme='http://www.blogger.com/atom/ns#' term='LaTeX'/><title type='text'>SEEing LaTeX 23: Am I Done?</title><content type='html'>Last July, &lt;a href="http://appliedabstraction.blogspot.com/2007/07/seeing-latex-5-taking-stock.html"&gt;I listed some desirable features&lt;/a&gt; for the LaTeX mode for &lt;a href="http://codingmonkeys.de/subethaedit"&gt;SubEthaEdit&lt;/a&gt;. The mentioned features were integrating with a PDF viewer using &lt;a href="http://itexmac.sourceforge.net/pdfsync.html"&gt;pdfsync&lt;/a&gt;, enabling insertion of citation keys in bibtex format, allow typesetting by calling &lt;code&gt;pdflatex&lt;/code&gt; from within SEE, cleaning up auxiliary files, and commenting out selected lines. I also mentioned that it might be nice, if inessential, to be able to insert environments and formatting. &lt;br /&gt;&lt;br /&gt;I've accomplished all of that. As well, I have introduced a mechanism for customizing the shell script environment for the mode and have developed a set of AppleScript handlers useful for scripting SEE. I'm quite pleased with how all of that has worked out. Not only do I now have a LaTeX mode that covers my main needs, but I've got a solid foundation on which I - and hopefully others! - can build support scripts for other modes.&lt;br /&gt;&lt;br /&gt;That said, the LaTeX mode is not quite finished. It's clear that I should write some documentation, and add a "Mode Help" menu item. I should also better document &lt;a href="http://appliedabstraction.blogspot.com/2008/01/seeing-latex-19-subethaedittools-or.html"&gt;SubEthaEditTools&lt;/a&gt;. Beyond that, there are two additional tasks.&lt;br /&gt;&lt;br /&gt;First, the scripts in the LaTeX mode menu should be put into some sort of reasonable order, rather than the haphazard order that currently is there. The items appear in order based on the names of the scripts, which can differ from the entry in the menu. What is the right order to use?&lt;br /&gt;&lt;br /&gt;Second, I've omitted some important elements of a LaTeX system, because &lt;a href="http://www.phys.psu.edu/~collins/software/latexmk-jcc/"&gt;&lt;code&gt;latexmk&lt;/code&gt;&lt;/a&gt; handles them for me. For example, I have not provided a way to run &lt;code&gt;bibtex&lt;/code&gt;. Should additional elements be added? If so, which? &lt;code&gt;bibtex&lt;/code&gt;? &lt;code&gt;makeindex&lt;/code&gt;? Something else?&lt;br /&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3164056879958238384-4376200904559517535?l=appliedabstraction.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://appliedabstraction.blogspot.com/feeds/4376200904559517535/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3164056879958238384&amp;postID=4376200904559517535' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/4376200904559517535'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/4376200904559517535'/><link rel='alternate' type='text/html' href='http://appliedabstraction.blogspot.com/2008/01/seeing-latex-23-am-i-done.html' title='SEEing LaTeX 23: Am I Done?'/><author><name>Michael Barber</name><uri>http://www.blogger.com/profile/15713879666980145844</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3164056879958238384.post-7322638536236400002</id><published>2008-01-05T09:25:00.001-08:00</published><updated>2008-01-05T09:25:49.852-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='SEEing LaTeX'/><category scheme='http://www.blogger.com/atom/ns#' term='AppleScript'/><category scheme='http://www.blogger.com/atom/ns#' term='SubEthaEdit'/><category scheme='http://www.blogger.com/atom/ns#' term='LaTeX'/><title type='text'>SEEing LaTeX 22: Inline Math</title><content type='html'>&lt;a href="http://appliedabstraction.blogspot.com/2008/01/seeing-latex-21-inserting-environments.html"&gt;Environments&lt;/a&gt; aren't the only thing common LaTeX constructs that are awkward to type. The delimiters for inline math are pretty awkward, too. Let's add those, too:&lt;br /&gt;&lt;div style="text-align:left;color:#000000; background-color:#ffffff; border:solid black 1px; padding:0.5em 1em 0.5em 1em; overflow:auto;"&gt;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;set&lt;/strong&gt;&lt;/span&gt; mathText &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;to&lt;/strong&gt;&lt;/span&gt; selectionText()&lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;set&lt;/strong&gt;&lt;/span&gt; wrappedText &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;to&lt;/strong&gt;&lt;/span&gt; &amp;quot;\\( &amp;quot; &amp;amp; mathText &amp;amp; &amp;quot; \\)&amp;quot;&lt;br /&gt; setSelectionText &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;to&lt;/strong&gt;&lt;/span&gt; wrappedText&lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;if&lt;/strong&gt;&lt;/span&gt; (length &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;of&lt;/strong&gt;&lt;/span&gt; mathText) &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;equals&lt;/strong&gt;&lt;/span&gt; 0 &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;then&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;set&lt;/strong&gt;&lt;/span&gt; {startChar, nextChar} &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;to&lt;/strong&gt;&lt;/span&gt; selectionRange without extendingFront &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;or&lt;/strong&gt;&lt;/span&gt; extendingEnd&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;setSelectionRange &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;to&lt;/strong&gt;&lt;/span&gt; (startChar + length &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;of&lt;/strong&gt;&lt;/span&gt; mathText + 3)&lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;end&lt;/strong&gt;&lt;/span&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;if&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt; &lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;on&lt;/strong&gt;&lt;/span&gt; seescriptsettings()&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{displayName:&amp;quot;Inline Math&amp;quot;, keyboardShortcut:&amp;quot;@~^m&amp;quot;, inContextMenu:&amp;quot;yes&amp;quot;}&lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;end&lt;/strong&gt;&lt;/span&gt; seescriptsettings&lt;br /&gt; &lt;br /&gt; include(`SubEthaEditTools.applescript')&lt;br /&gt; &lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3164056879958238384-7322638536236400002?l=appliedabstraction.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://appliedabstraction.blogspot.com/feeds/7322638536236400002/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3164056879958238384&amp;postID=7322638536236400002' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/7322638536236400002'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/7322638536236400002'/><link rel='alternate' type='text/html' href='http://appliedabstraction.blogspot.com/2008/01/seeing-latex-22-inline-math.html' title='SEEing LaTeX 22: Inline Math'/><author><name>Michael Barber</name><uri>http://www.blogger.com/profile/15713879666980145844</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3164056879958238384.post-8908735643967523182</id><published>2008-01-05T09:05:00.001-08:00</published><updated>2008-01-19T10:15:14.098-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='SEEing LaTeX'/><category scheme='http://www.blogger.com/atom/ns#' term='AppleScript'/><category scheme='http://www.blogger.com/atom/ns#' term='SubEthaEdit'/><category scheme='http://www.blogger.com/atom/ns#' term='LaTeX'/><title type='text'>SEEing LaTeX 21: Inserting Environments</title><content type='html'>Environments again? Yes, but this time we're going to look at environments in LaTeX, not the shell environment. Environments are a bit of a pain to type, but the repetitive structure makes them suitable to automation: we just get the name of the environment, and then have a more-or-less standard form:&lt;br /&gt;&lt;div style="text-align:left;color:#000000; background-color:#ffffff; border:solid black 1px; padding:0.5em 1em 0.5em 1em; overflow:auto;"&gt;&lt;span style="color:#881350;"&gt;\begin&lt;/span&gt;&lt;span style="color:#440088;"&gt;{environmentname}&lt;/span&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;body&lt;br /&gt; &lt;span style="color:#881350;"&gt;\end&lt;/span&gt;&lt;span style="color:#440088;"&gt;{environmentname}&lt;/span&gt;&lt;br /&gt; &lt;/div&gt;&lt;br /&gt;Indentation of the body can be a little unclear. For example, I usually indent equation environments, but would never dream of indenting the document environment. &lt;br /&gt;&lt;br /&gt;To add environment insertion into the LaTeX mode for &lt;a href="http://codingmonkeys.de/subethaedit"&gt;SubEthaEdit&lt;/a&gt;, we begin by getting the environment name using &lt;code&gt;display dialog&lt;/code&gt;:&lt;br /&gt;&lt;div style="text-align:left;color:#000000; background-color:#ffffff; border:solid black 1px; padding:0.5em 1em 0.5em 1em; overflow:auto;"&gt;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;try&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;display dialog &amp;quot;Enter environment name:&amp;quot; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;with&lt;/strong&gt;&lt;/span&gt; title &amp;quot;Insert Environment&amp;quot; default answer &amp;quot;equation&amp;quot;&lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;on&lt;/strong&gt;&lt;/span&gt; &lt;span style="color:#0b0bff;"&gt;error&lt;/span&gt; number -128 &lt;span style="color:#8d8d8d;"&gt;&lt;em&gt;-- user canceled&lt;br /&gt; &lt;/em&gt;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;return&lt;/span&gt;&lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;end&lt;/strong&gt;&lt;/span&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;try&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;set&lt;/strong&gt;&lt;/span&gt; envName &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;to&lt;/strong&gt;&lt;/span&gt; text returned &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;of&lt;/strong&gt;&lt;/span&gt; &lt;span style="color:#0b0bff;"&gt;result&lt;/span&gt;&lt;br /&gt; &lt;/div&gt;&lt;br /&gt;The &lt;code&gt;try&lt;/code&gt; block is to handle when the user cancels instead of entering and environment name. Somewhat arbitrarily, I set the default answer to be "equation", since my guess is that equations are probably the most common environment. &lt;br /&gt;&lt;br /&gt;After that, there is just some fiddly work getting the formatting of the environment correct. It needs to intelligently insert newlines and tabs to keep the document readable. As well, something needs to be done with the selection text. There are two possibilities as I see it: (1) treat the selection as the name of the environment and (2) treat the selection as the body of the environment. I went with the latter. Finally, it would be nice to place the insertion point somewhere reasonable; I think it works nicely at the end of the body, especially since the insertion point is positioned to start typing immediately if the body is empty. Putting it all together, we have:&lt;br /&gt;&lt;div style="text-align:left;color:#000000; background-color:#ffffff; border:solid black 1px; padding:0.5em 1em 0.5em 1em; overflow:auto;"&gt;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;set&lt;/strong&gt;&lt;/span&gt; {startSelected, nextSelected} &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;to&lt;/strong&gt;&lt;/span&gt; selectionRange without extendingFront &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;or&lt;/strong&gt;&lt;/span&gt; extendingEnd&lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;set&lt;/strong&gt;&lt;/span&gt; {startExtended, nextExtended} &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;to&lt;/strong&gt;&lt;/span&gt; selectionRange &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;with&lt;/strong&gt;&lt;/span&gt; extendingFront &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;and&lt;/strong&gt;&lt;/span&gt; extendingEnd&lt;br /&gt; &lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;set&lt;/strong&gt;&lt;/span&gt; prefix &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;to&lt;/strong&gt;&lt;/span&gt; selectByComparing(startSelected, startExtended, &amp;quot;&amp;quot;, &amp;quot;\n&amp;quot;)&lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;set&lt;/strong&gt;&lt;/span&gt; suffix &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;to&lt;/strong&gt;&lt;/span&gt; selectByComparing(nextSelected, nextExtended, &amp;quot;&amp;quot;, &amp;quot;\n&amp;quot;)&lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;set&lt;/strong&gt;&lt;/span&gt; indent &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;to&lt;/strong&gt;&lt;/span&gt; selectByComparing(startSelected, nextSelected, &amp;quot;\t&amp;quot;, &amp;quot;&amp;quot;)&lt;br /&gt; &lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;set&lt;/strong&gt;&lt;/span&gt; beforeInsertion &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;to&lt;/strong&gt;&lt;/span&gt; (join &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;of&lt;/strong&gt;&lt;/span&gt; {prefix, &amp;quot;\\begin{&amp;quot;, envName, &amp;quot;}\n&amp;quot;, indent, selectionText()} by &amp;quot;&amp;quot;)&lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;set&lt;/strong&gt;&lt;/span&gt; afterInsertion &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;to&lt;/strong&gt;&lt;/span&gt; (join &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;of&lt;/strong&gt;&lt;/span&gt; {suffix, &amp;quot;\\end{&amp;quot;, envName, &amp;quot;}\n&amp;quot; } by &amp;quot;&amp;quot;)&lt;br /&gt; &lt;br /&gt; setSelectionText &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;to&lt;/strong&gt;&lt;/span&gt; (beforeInsertion &amp;amp; afterInsertion)&lt;br /&gt; setSelectionRange &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;to&lt;/strong&gt;&lt;/span&gt; startSelected + (&lt;span style="color:#0b0bff;"&gt;count&lt;/span&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;of&lt;/strong&gt;&lt;/span&gt; beforeInsertion) - 1 + (&lt;span style="color:#0b0bff;"&gt;count&lt;/span&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;of&lt;/strong&gt;&lt;/span&gt; suffix)&lt;br /&gt; &lt;/div&gt; Note that I've made repeated use of a convenience function:&lt;br /&gt;&lt;div style="text-align:left;color:#000000; background-color:#ffffff; border:solid black 1px; padding:0.5em 1em 0.5em 1em; overflow:auto;"&gt;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;to&lt;/strong&gt;&lt;/span&gt; selectByComparing(val1, val2, sameVal, diffVal)&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;if&lt;/strong&gt;&lt;/span&gt; val1 &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;equals&lt;/strong&gt;&lt;/span&gt; val2 &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;then&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sameVal&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;else&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;diffVal&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;end&lt;/strong&gt;&lt;/span&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;if&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;end&lt;/strong&gt;&lt;/span&gt; selectByComparing&lt;br /&gt; &lt;/div&gt;&lt;br /&gt;The &lt;code&gt;selectByComparing&lt;/code&gt; handler is not part of &lt;a href="http://appliedabstraction.blogspot.com/2008/01/seeing-latex-19-subethaedittools-or.html"&gt;SubEthaEditTools&lt;/a&gt; - maybe it should be?&lt;br /&gt;&lt;br /&gt;That's all there is to it, apart from the boilerplate:&lt;br /&gt;&lt;div style="text-align:left;color:#000000; background-color:#ffffff; border:solid black 1px; padding:0.5em 1em 0.5em 1em; overflow:auto;"&gt;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;on&lt;/strong&gt;&lt;/span&gt; seescriptsettings()&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{displayName:&amp;quot;Insert Environment...&amp;quot;, keyboardShortcut:&amp;quot;@^e&amp;quot;, inContextMenu:&amp;quot;yes&amp;quot;}&lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;end&lt;/strong&gt;&lt;/span&gt; seescriptsettings&lt;br /&gt; &lt;br /&gt; include(`SubEthaEditTools.applescript')&lt;br /&gt; &lt;/div&gt; &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Update:&lt;/strong&gt; There is an interesting possibility for adjusting the default environment. I had used "equation" as the default, but it was pretty arbitrary. Another approach would be to just repeat whichever environment was last given. We just add a property to the script that holds the default environment, use its value in making the dialog, and  update the property based on the dialog result:&lt;br /&gt;&lt;div style="text-align:left;color:#000000; background-color:#ffffff; border:solid black 1px; padding:0.5em 1em 0.5em 1em; overflow:auto;"&gt;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;property&lt;/strong&gt;&lt;/span&gt; defaultEnvironment: &amp;quot;equation&amp;quot;&lt;br /&gt; &lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;try&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;display dialog &amp;quot;Enter environment name:&amp;quot; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;with&lt;/strong&gt;&lt;/span&gt; title &amp;quot;Insert Environment&amp;quot; default answer defaultEnvironment&lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;on&lt;/strong&gt;&lt;/span&gt; &lt;span style="color:#0b0bff;"&gt;error&lt;/span&gt; number -128 &lt;span style="color:#8d8d8d;"&gt;&lt;em&gt;-- user canceled&lt;br /&gt; &lt;/em&gt;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;return&lt;/span&gt;&lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;end&lt;/strong&gt;&lt;/span&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;try&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;set&lt;/strong&gt;&lt;/span&gt; envName &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;to&lt;/strong&gt;&lt;/span&gt; text returned &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;of&lt;/strong&gt;&lt;/span&gt; &lt;span style="color:#0b0bff;"&gt;result&lt;/span&gt;&lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;set&lt;/strong&gt;&lt;/span&gt; defaultEnvironment &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;to&lt;/strong&gt;&lt;/span&gt; envName&lt;br /&gt; &lt;/div&gt;&lt;br /&gt;Is this actually a good idea? or will it just be annoying? Hard to say without using it, so I guess I'll try it out for a while.&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3164056879958238384-8908735643967523182?l=appliedabstraction.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://appliedabstraction.blogspot.com/feeds/8908735643967523182/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3164056879958238384&amp;postID=8908735643967523182' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/8908735643967523182'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/8908735643967523182'/><link rel='alternate' type='text/html' href='http://appliedabstraction.blogspot.com/2008/01/seeing-latex-21-inserting-environments.html' title='SEEing LaTeX 21: Inserting Environments'/><author><name>Michael Barber</name><uri>http://www.blogger.com/profile/15713879666980145844</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3164056879958238384.post-4494235436149096859</id><published>2008-01-04T12:38:00.001-08:00</published><updated>2008-01-04T12:38:05.695-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='BibDesk'/><category scheme='http://www.blogger.com/atom/ns#' term='SEEing LaTeX'/><category scheme='http://www.blogger.com/atom/ns#' term='AppleScript'/><category scheme='http://www.blogger.com/atom/ns#' term='SubEthaEdit'/><category scheme='http://www.blogger.com/atom/ns#' term='LaTeX'/><title type='text'>SEEing LaTeX 20: Getting Citations Right</title><content type='html'>I started enhancing the LaTeX mode for &lt;a href="http://codingmonkeys.de/subethaedit"&gt;SubEthaEdit&lt;/a&gt; by looking at &lt;a href="http://appliedabstraction.blogspot.com/2007/06/seeing-latex-1-citations.html"&gt;citations&lt;/a&gt;. The original approach, based on just using the &lt;a href="http://bibdesk.sourceforge.net/manual/BibDesk%20Help_51.html#SEC99"&gt;input manager&lt;/a&gt; supplied with &lt;a href="http://bibdesk.sourceforge.net/"&gt;BibDesk&lt;/a&gt;, proved to be &lt;a href="http://appliedabstraction.blogspot.com/2007/07/seeing-latex-4-more-on-completions.html"&gt;unsatisfactory&lt;/a&gt;. Let's fix that now.&lt;br /&gt;&lt;br /&gt;The general approach seems clear enough. We need to determine a search term based on the cursor position in the LaTeX document, pass that to BibDesk to get matching documents, let the user pick which documents are relevant, format the selected documents, and insert the result into the document. Using &lt;a href="http://appliedabstraction.blogspot.com/2008/01/seeing-latex-19-subethaedittools-or.html"&gt;SubEthaEditTools&lt;/a&gt; our &lt;a href="http://appliedabstraction.blogspot.com/2007/12/seeing-latex-18-cutting-subethaedit-out.html"&gt;SEE scripting abstraction layer&lt;/a&gt;, it proves to be fairly straightforward. &lt;br /&gt;&lt;br /&gt;The first step, determining the search term, is probably the trickiest. What I envision is that the user can enter a partial citation and have it finished by the script. Thus, we'll need to examine the text immediately before the insertion point and determine a partial citation key. We should not cross an open brace "{", as we don't want to include the macro. Additionally, we shouldn't cross a comma, since we might be looking at multiple citations within one macro. Let's &lt;em&gt;not&lt;/em&gt; check the calling macro; this allows the completion to be invoked at inappropriate points, but also allows the completion to be invoked for user-defined macros.&lt;br /&gt;&lt;br /&gt;One final issue is what we do when there is a range of text selected. Since the default behavior should be to have no text selected, we should treat a non-empty selection as meaningful. Let's take it to mean that the search should be constrained to only include the selected text. Therefore, we determine a search term based either on the selected text or all the text on the line preceding the insertion point. &lt;br /&gt;&lt;br /&gt;Putting all that together, I came up with:&lt;br /&gt;&lt;div style="text-align:left;color:#000000; background-color:#ffffff; border:solid black 1px; padding:0.5em 1em 0.5em 1em; overflow:auto;"&gt;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;set&lt;/strong&gt;&lt;/span&gt; {startChar, nextChar} &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;to&lt;/strong&gt;&lt;/span&gt; selectionRange without extendingFront &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;or&lt;/strong&gt;&lt;/span&gt; extendingEnd&lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;if&lt;/strong&gt;&lt;/span&gt; startChar &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;equals&lt;/strong&gt;&lt;/span&gt; nextChar &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;then&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#8d8d8d;"&gt;&lt;em&gt;-- empty selection, try the whole line&lt;br /&gt; &lt;/em&gt;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;set&lt;/strong&gt;&lt;/span&gt; selectionContents &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;to&lt;/strong&gt;&lt;/span&gt; extendedSelectionText &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;with&lt;/strong&gt;&lt;/span&gt; extendingFront without extendingEnd&lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;else&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;set&lt;/strong&gt;&lt;/span&gt; selectionContents &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;to&lt;/strong&gt;&lt;/span&gt; selectionText()&lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;end&lt;/strong&gt;&lt;/span&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;if&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;set&lt;/strong&gt;&lt;/span&gt; macroArgument &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;to&lt;/strong&gt;&lt;/span&gt; the &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;last&lt;/strong&gt;&lt;/span&gt; item &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;of&lt;/strong&gt;&lt;/span&gt; the (tokens &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;of&lt;/strong&gt;&lt;/span&gt; the selectionContents between &amp;quot;{&amp;quot;)&lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;set&lt;/strong&gt;&lt;/span&gt; searchTerm &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;to&lt;/strong&gt;&lt;/span&gt; the &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;last&lt;/strong&gt;&lt;/span&gt; item &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;of&lt;/strong&gt;&lt;/span&gt; the (tokens &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;of&lt;/strong&gt;&lt;/span&gt; the macroArgument between &amp;quot;,&amp;quot;)&lt;br /&gt; &lt;/div&gt;&lt;br /&gt;I've used a couple of the new handlers from &lt;a href="http://appliedabstraction.blogspot.com/2008/01/seeing-latex-19-subethaedittools-or.html"&gt;SubEthaEditTools&lt;/a&gt;. These are hopefully self-explanatory, but check the implementation in case they are not.&lt;br /&gt;&lt;br /&gt;Using the two &lt;code&gt;tokens&lt;/code&gt; calls, we obtain a partial citation key. We pass that to BibDesk:&lt;br /&gt;&lt;div style="text-align:left;color:#000000; background-color:#ffffff; border:solid black 1px; padding:0.5em 1em 0.5em 1em; overflow:auto;"&gt;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;tell&lt;/strong&gt;&lt;/span&gt; &lt;span style="color:#0b0bff;"&gt;application&lt;/span&gt; &amp;quot;BibDesk&amp;quot;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;set&lt;/strong&gt;&lt;/span&gt; citeMatches &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;to&lt;/strong&gt;&lt;/span&gt; search for searchTerm &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;with&lt;/strong&gt;&lt;/span&gt; for completion&lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;end&lt;/strong&gt;&lt;/span&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;tell&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt; &lt;/div&gt;&lt;br /&gt;The ungrammatical &lt;code&gt;with for completion&lt;/code&gt; will give us a list of cite keys, not just document titles. Each completion is given as a string containing both the cite key and some document information, separated by a " % " string. &lt;br /&gt;&lt;br /&gt;We next present the list of completions to the user, in order to narrow the list down to just the appropriate citations. To present a list, we use &lt;code&gt;choose from list&lt;/code&gt; from the AppleScript StandardAdditions. There are three cases worth considering. First, if there is only one publication, we can select it by default, so the user just confirms whether it is correct. Second, if there are multiple possible publications, we just show the list, declining to guess. Finally, if there aren't any matches, we just inform the user with &lt;code&gt;display alert&lt;/code&gt;; in this case, we'll set the list of publications to the empty list. If there aren't any publications returned, that means the user canceled, so we should just exit and leave the document unchanged. Putting it all together, we arrive at:&lt;br /&gt;&lt;div style="text-align:left;color:#000000; background-color:#ffffff; border:solid black 1px; padding:0.5em 1em 0.5em 1em; overflow:auto;"&gt;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;if&lt;/strong&gt;&lt;/span&gt; (&lt;span style="color:#0b0bff;"&gt;count&lt;/span&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;of&lt;/strong&gt;&lt;/span&gt; citeMatches) &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;equals&lt;/strong&gt;&lt;/span&gt; 1 &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;then&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;choose from list citeMatches &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;with&lt;/strong&gt;&lt;/span&gt; title &amp;quot;Citation Matches&amp;quot; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;with&lt;/strong&gt;&lt;/span&gt; prompt &amp;quot;One matching publication:&amp;quot; default items citeMatches&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;set&lt;/strong&gt;&lt;/span&gt; pubs &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;to&lt;/strong&gt;&lt;/span&gt; &lt;span style="color:#0b0bff;"&gt;result&lt;/span&gt;&lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;else&lt;/strong&gt;&lt;/span&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;if&lt;/strong&gt;&lt;/span&gt; (&lt;span style="color:#0b0bff;"&gt;count&lt;/span&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;of&lt;/strong&gt;&lt;/span&gt; citeMatches) &amp;gt; 1&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;choose from list citeMatches &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;with&lt;/strong&gt;&lt;/span&gt; title &amp;quot;Citation Matches&amp;quot; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;with&lt;/strong&gt;&lt;/span&gt; prompt &amp;quot;Please select publications:&amp;quot; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;with&lt;/strong&gt;&lt;/span&gt; multiple selections allowed&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;set&lt;/strong&gt;&lt;/span&gt; pubs &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;to&lt;/strong&gt;&lt;/span&gt; &lt;span style="color:#0b0bff;"&gt;result&lt;/span&gt;&lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;else&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;display alert &amp;quot;No matches found for partial citation \&amp;quot;&amp;quot; &amp;amp; searchTerm &amp;amp; &amp;quot;\&amp;quot;&amp;quot;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;set&lt;/strong&gt;&lt;/span&gt; pubs &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;to&lt;/strong&gt;&lt;/span&gt; {}&lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;end&lt;/strong&gt;&lt;/span&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;if&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt; &lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;if&lt;/strong&gt;&lt;/span&gt; (&lt;span style="color:#0b0bff;"&gt;count&lt;/span&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;of&lt;/strong&gt;&lt;/span&gt; pubs) &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;equals&lt;/strong&gt;&lt;/span&gt; 0 &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;then&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#8d8d8d;"&gt;&lt;em&gt;-- user canceled, do nothing&lt;br /&gt; &lt;/em&gt;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;return&lt;/span&gt;&lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;end&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt; &lt;/div&gt;&lt;br /&gt;&lt;br /&gt; We now have a non-empty list of completions. We need to split those apart to get the cite keys, then join the cite keys with commas. I used &lt;code&gt;awk&lt;/code&gt;:&lt;br /&gt;&lt;div style="text-align:left;color:#000000; background-color:#ffffff; border:solid black 1px; padding:0.5em 1em 0.5em 1em; overflow:auto;"&gt;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;set&lt;/strong&gt;&lt;/span&gt; citation &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;to&lt;/strong&gt;&lt;/span&gt; shellTransform &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;of&lt;/strong&gt;&lt;/span&gt; (join &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;of&lt;/strong&gt;&lt;/span&gt; pubs by &amp;quot;\n&amp;quot;) for &amp;quot;&amp;quot; through &amp;quot;awk -F' % ' 'NR == 1 { printf(\&amp;quot;%s\&amp;quot;, $1) } NR &amp;gt; 1 { printf(\&amp;quot;,%s\&amp;quot;, $1) }'&amp;quot; without alteringLineEndings&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;Pretty ugly! Let's reformat the core of the &lt;code&gt;awk&lt;/code&gt; program to make it clearer:&lt;br /&gt;&lt;div style="text-align:left;color:#000000; background-color:#ffffff; border:solid black 1px; padding:0.5em 1em 0.5em 1em; overflow:auto;font-size:small; font-family:monospace; "&gt;&lt;span style="color:#760f15;"&gt;NR == 1 { printf(&amp;quot;%s&amp;quot;, &lt;/span&gt;&lt;span style="color:#c4620a;"&gt;$1&lt;/span&gt;&lt;span style="color:#760f15;"&gt;) } &lt;br /&gt; NR &amp;gt; 1 { printf(&amp;quot;,%s&amp;quot;, &lt;/span&gt;&lt;span style="color:#c4620a;"&gt;$1&lt;/span&gt;&lt;span style="color:#760f15;"&gt;) }&lt;br /&gt; &lt;/span&gt;&lt;/div&gt;&lt;br /&gt;In this form, it's clear enough, keeping in mind that we use " % " as the field separator: we just print out the first field, corresponding to the cite key, with the first record treated specially to get the number of commas right. &lt;br /&gt;&lt;br /&gt;Finally, we insert the formatted citation back into the document. I also move the insertion point to the end of the formatted citation, as a typing convenience. I had considered closing the brace for the citation macro, but that would make multi-citation lists more awkward, so decided against it. This is none too difficult:&lt;br /&gt;&lt;div style="text-align:left;color:#000000; background-color:#ffffff; border:solid black 1px; padding:0.5em 1em 0.5em 1em; overflow:auto;"&gt;setSelectionRange &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;to&lt;/strong&gt;&lt;/span&gt; {nextChar - (length &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;of&lt;/strong&gt;&lt;/span&gt; searchTerm), nextChar - 1}&lt;br /&gt; setSelectionText &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;to&lt;/strong&gt;&lt;/span&gt; citation&lt;br /&gt; setSelectionRange &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;to&lt;/strong&gt;&lt;/span&gt; (nextChar - (length &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;of&lt;/strong&gt;&lt;/span&gt; searchTerm) + (length &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;of&lt;/strong&gt;&lt;/span&gt; citation))&lt;br /&gt; &lt;/div&gt;&lt;br /&gt;Again, I've used new handlers from the SubEthaEditTools library.&lt;br /&gt;&lt;br /&gt;Putting it all together, and adding a &lt;code&gt;seescriptsettings&lt;/code&gt; handler to integrate it into SEE, we get:&lt;br /&gt;&lt;div style="text-align:left;color:#000000; background-color:#ffffff; border:solid black 1px; padding:0.5em 1em 0.5em 1em; overflow:auto;"&gt;&lt;span style="color:#8d8d8d;"&gt;&lt;em&gt;-- $Id: BibDeskCompletions.applescript,v 1.2 2008/01/04 18:40:16 mjb Exp mjb $&lt;br /&gt; &lt;/em&gt;&lt;/span&gt;&lt;br /&gt; &lt;span style="color:#8d8d8d;"&gt;&lt;em&gt;(*&lt;br /&gt; Need to figure out the search term. Treat a selection as meaning to constrain the search &lt;br /&gt; term to lie within the selection, and an empty selection as meaning to get the search term&lt;br /&gt; from the preceding text on the line. We don't cross an opening brace, so that the search term&lt;br /&gt; comes from a call to a macro. However, we don't check to see if the macro is one of the &lt;br /&gt; standard citation macros, since we do want to allow user macros. &lt;br /&gt; *)&lt;/em&gt;&lt;/span&gt;&lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;set&lt;/strong&gt;&lt;/span&gt; {startChar, nextChar} &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;to&lt;/strong&gt;&lt;/span&gt; selectionRange without extendingFront &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;or&lt;/strong&gt;&lt;/span&gt; extendingEnd&lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;if&lt;/strong&gt;&lt;/span&gt; startChar &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;equals&lt;/strong&gt;&lt;/span&gt; nextChar &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;then&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#8d8d8d;"&gt;&lt;em&gt;-- empty selection, try the whole line&lt;br /&gt; &lt;/em&gt;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;set&lt;/strong&gt;&lt;/span&gt; selectionContents &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;to&lt;/strong&gt;&lt;/span&gt; extendedSelectionText &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;with&lt;/strong&gt;&lt;/span&gt; extendingFront without extendingEnd&lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;else&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;set&lt;/strong&gt;&lt;/span&gt; selectionContents &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;to&lt;/strong&gt;&lt;/span&gt; selectionText()&lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;end&lt;/strong&gt;&lt;/span&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;if&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;set&lt;/strong&gt;&lt;/span&gt; macroArgument &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;to&lt;/strong&gt;&lt;/span&gt; the &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;last&lt;/strong&gt;&lt;/span&gt; item &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;of&lt;/strong&gt;&lt;/span&gt; the (tokens &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;of&lt;/strong&gt;&lt;/span&gt; the selectionContents between &amp;quot;{&amp;quot;)&lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;set&lt;/strong&gt;&lt;/span&gt; searchTerm &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;to&lt;/strong&gt;&lt;/span&gt; the &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;last&lt;/strong&gt;&lt;/span&gt; item &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;of&lt;/strong&gt;&lt;/span&gt; the (tokens &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;of&lt;/strong&gt;&lt;/span&gt; the macroArgument between &amp;quot;,&amp;quot;)&lt;br /&gt; &lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;tell&lt;/strong&gt;&lt;/span&gt; &lt;span style="color:#0b0bff;"&gt;application&lt;/span&gt; &amp;quot;BibDesk&amp;quot;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;set&lt;/strong&gt;&lt;/span&gt; citeMatches &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;to&lt;/strong&gt;&lt;/span&gt; search for searchTerm &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;with&lt;/strong&gt;&lt;/span&gt; for completion&lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;end&lt;/strong&gt;&lt;/span&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;tell&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt; &lt;br /&gt; &lt;span style="color:#8d8d8d;"&gt;&lt;em&gt;-- get list of publications, customizing user interaction based on number of matches&lt;br /&gt; &lt;/em&gt;&lt;/span&gt;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;if&lt;/strong&gt;&lt;/span&gt; (&lt;span style="color:#0b0bff;"&gt;count&lt;/span&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;of&lt;/strong&gt;&lt;/span&gt; citeMatches) &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;equals&lt;/strong&gt;&lt;/span&gt; 1 &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;then&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;choose from list citeMatches &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;with&lt;/strong&gt;&lt;/span&gt; title &amp;quot;Citation Matches&amp;quot; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;with&lt;/strong&gt;&lt;/span&gt; prompt &amp;quot;One matching publication:&amp;quot; default items citeMatches&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;set&lt;/strong&gt;&lt;/span&gt; pubs &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;to&lt;/strong&gt;&lt;/span&gt; &lt;span style="color:#0b0bff;"&gt;result&lt;/span&gt;&lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;else&lt;/strong&gt;&lt;/span&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;if&lt;/strong&gt;&lt;/span&gt; (&lt;span style="color:#0b0bff;"&gt;count&lt;/span&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;of&lt;/strong&gt;&lt;/span&gt; citeMatches) &amp;gt; 1&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;choose from list citeMatches &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;with&lt;/strong&gt;&lt;/span&gt; title &amp;quot;Citation Matches&amp;quot; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;with&lt;/strong&gt;&lt;/span&gt; prompt &amp;quot;Please select publications:&amp;quot; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;with&lt;/strong&gt;&lt;/span&gt; multiple selections allowed&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;set&lt;/strong&gt;&lt;/span&gt; pubs &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;to&lt;/strong&gt;&lt;/span&gt; &lt;span style="color:#0b0bff;"&gt;result&lt;/span&gt;&lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;else&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;display alert &amp;quot;No matches found for partial citation \&amp;quot;&amp;quot; &amp;amp; searchTerm &amp;amp; &amp;quot;\&amp;quot;&amp;quot;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;set&lt;/strong&gt;&lt;/span&gt; pubs &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;to&lt;/strong&gt;&lt;/span&gt; {}&lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;end&lt;/strong&gt;&lt;/span&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;if&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt; &lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;if&lt;/strong&gt;&lt;/span&gt; (&lt;span style="color:#0b0bff;"&gt;count&lt;/span&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;of&lt;/strong&gt;&lt;/span&gt; pubs) &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;equals&lt;/strong&gt;&lt;/span&gt; 0 &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;then&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#8d8d8d;"&gt;&lt;em&gt;-- user canceled, do nothing&lt;br /&gt; &lt;/em&gt;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;return&lt;/span&gt;&lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;end&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt; &lt;br /&gt; &lt;span style="color:#8d8d8d;"&gt;&lt;em&gt;(* &lt;br /&gt; At this point, there is a non-empty list of matches, which replaces the search term. By &lt;br /&gt; construction, the search term always immediately precedes the end of the selection.&lt;br /&gt; Call out to the shell to format the publication list into a LaTeX citation, insert the citation,&lt;br /&gt; and then move the insertion point just after the citation. &lt;br /&gt; *)&lt;/em&gt;&lt;/span&gt;&lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;set&lt;/strong&gt;&lt;/span&gt; citation &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;to&lt;/strong&gt;&lt;/span&gt; shellTransform &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;of&lt;/strong&gt;&lt;/span&gt; (join &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;of&lt;/strong&gt;&lt;/span&gt; pubs by &amp;quot;\n&amp;quot;) for &amp;quot;&amp;quot; through &amp;quot;awk -F' % ' 'NR == 1 { printf(\&amp;quot;%s\&amp;quot;, $1) } NR &amp;gt; 1 { printf(\&amp;quot;,%s\&amp;quot;, $1) }'&amp;quot; without alteringLineEndings&lt;br /&gt; setSelectionRange &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;to&lt;/strong&gt;&lt;/span&gt; {nextChar - (length &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;of&lt;/strong&gt;&lt;/span&gt; searchTerm), nextChar - 1}&lt;br /&gt; setSelectionText &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;to&lt;/strong&gt;&lt;/span&gt; citation&lt;br /&gt; setSelectionRange &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;to&lt;/strong&gt;&lt;/span&gt; (nextChar - (length &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;of&lt;/strong&gt;&lt;/span&gt; searchTerm) + (length &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;of&lt;/strong&gt;&lt;/span&gt; citation))&lt;br /&gt; &lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;on&lt;/strong&gt;&lt;/span&gt; seescriptsettings()&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{displayName:&amp;quot;Complete Citation&amp;quot;, shortDisplayName:&amp;quot;Citation&amp;quot;, keyboardShortcut:&amp;quot;@^j&amp;quot;, &amp;nbsp;toolbarIcon:&amp;quot;ToolbarBibDesk.png&amp;quot;, inDefaultToolbar:&amp;quot;yes&amp;quot;, toolbarTooltip:&amp;quot;Complete citation using BibDesk&amp;quot;, inContextMenu:&amp;quot;yes&amp;quot;}&lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;end&lt;/strong&gt;&lt;/span&gt; seescriptsettings&lt;br /&gt; &lt;br /&gt; include(`SubEthaEditTools.applescript')&lt;br /&gt; &lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3164056879958238384-4494235436149096859?l=appliedabstraction.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://appliedabstraction.blogspot.com/feeds/4494235436149096859/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3164056879958238384&amp;postID=4494235436149096859' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/4494235436149096859'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/4494235436149096859'/><link rel='alternate' type='text/html' href='http://appliedabstraction.blogspot.com/2008/01/seeing-latex-20-getting-citations-right.html' title='SEEing LaTeX 20: Getting Citations Right'/><author><name>Michael Barber</name><uri>http://www.blogger.com/profile/15713879666980145844</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3164056879958238384.post-5813706735439064883</id><published>2008-01-04T12:32:00.001-08:00</published><updated>2008-04-23T12:46:14.274-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='m4'/><category scheme='http://www.blogger.com/atom/ns#' term='SEEing LaTeX'/><category scheme='http://www.blogger.com/atom/ns#' term='AppleScript'/><category scheme='http://www.blogger.com/atom/ns#' term='SubEthaEdit'/><category scheme='http://www.blogger.com/atom/ns#' term='abstraction'/><title type='text'>SEEing LaTeX 19: SubEthaEditTools, or, Eating My Own Dogfood</title><content type='html'>Let's take the arguments in the &lt;a href="http://appliedabstraction.blogspot.com/2007/12/seeing-latex-17-bit-more-on.html"&gt;last&lt;/a&gt; &lt;a href="http://appliedabstraction.blogspot.com/2007/12/seeing-latex-18-cutting-subethaedit-out.html"&gt;three&lt;/a&gt; &lt;a href="http://appliedabstraction.blogspot.com/2008/01/what-role-should-script-editor-play-in.html"&gt;posts&lt;/a&gt; seriously. We move all of the handlers developed in the course of the &lt;a href="http://appliedabstraction.blogspot.com/search/label/SEEing%20LaTeX"&gt;SEEing LaTeX&lt;/a&gt; series into a separate file called SubEthaEditTools.applescript, and rewrite all of the AppleScripts to include the file using &lt;code&gt;m4&lt;/code&gt;. While we're at it, we also add some more handlers to the SubEthaEditTools to try to make a more complete set, with the goal being to write scripts for the mode by using the handlers without directly interacting with SubEthaEdit in the AppleScript.&lt;br /&gt;&lt;br /&gt;This requires relatively minor changes to the makefile I've been using, but nothing too serious. Also, I construct SubEthaEditTools itself using &lt;code&gt;m4&lt;/code&gt;, with more general purpose handlers for, e.g., string manipulation placed in their own files. With the more modular structure, some additional minor rewrites of some handlers seems appropriate, which do not warrant specific comment. &lt;br /&gt;&lt;br /&gt;A concept that is made explicit in the SubEthaEditTools is that of the &lt;em&gt;extended selection&lt;/em&gt;. That is, the selection modified either so that the beginning is extended to the start of the first line of the selection, or to the end of the last line or the selection, or both. The extended selection has appeared implicitly several times, so it seems worthwhile to make it explicit. Further, there are now handlers both for extending the selection (forward or backward) and for referring to the extended selection without modifying the actual selection. &lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Update:&lt;/strong&gt; I've replaced the &lt;code&gt;quotedForm&lt;/code&gt; handler with a &lt;code&gt;doubleQuotedForm&lt;/code&gt; handler, to help prevent confusion with the &lt;code&gt;quoted form of&lt;/code&gt; action for AppleScript strings. I've also worked in a few usages of &lt;code&gt;quoted form of&lt;/code&gt; in the various scripts for the LaTeX mode (I didn't know about &lt;code&gt;quoted form of&lt;/code&gt; until recently).&lt;br /&gt;&lt;br /&gt;Also, I've added a license. It's an MIT-style license, so should be suitably permissive for use by others.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Update 2 (2008/04/23):&lt;/strong&gt;  I've added a few more handlers. Specifically, &lt;code&gt;modeSetting&lt;/code&gt;, &lt;code&gt;selectionIsEmpty&lt;/code&gt;, and &lt;code&gt;documentIsAvailable&lt;/code&gt;. &lt;br /&gt;&lt;br /&gt;&lt;div style="text-align:left;color:#000000; background-color:#ffffff; border:solid black 1px; padding:0.5em 1em 0.5em 1em; overflow:auto;"&gt;&lt;span style="color:#8d8d8d;"&gt;&lt;em&gt;-- SubEthaEdit Tools&lt;br /&gt; &lt;/em&gt;&lt;/span&gt;&lt;br /&gt; &lt;span style="color:#8d8d8d;"&gt;&lt;em&gt;(*&lt;br /&gt; Copyright (c) 2008, Michael J. Barber&lt;br /&gt; &lt;br /&gt; Permission is hereby granted, free of charge, to any person obtaining&lt;br /&gt; a copy of this software and associated documentation files (the&lt;br /&gt; &amp;quot;Software&amp;quot;), to deal in the Software without restriction, including&lt;br /&gt; without limitation the rights to use, copy, modify, merge, publish,&lt;br /&gt; distribute, sublicense, and/or sell copies of the Software, and to&lt;br /&gt; permit persons to whom the Software is furnished to do so, subject&lt;br /&gt; to the following conditions:&lt;br /&gt; &lt;br /&gt; The above copyright notice and this permission notice shall be&lt;br /&gt; included in all copies or substantial portions of the Software.&lt;br /&gt; &lt;br /&gt; THE SOFTWARE IS PROVIDED &amp;quot;AS IS&amp;quot;, WITHOUT WARRANTY OF ANY KIND,&lt;br /&gt; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF&lt;br /&gt; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.&lt;br /&gt; IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR&lt;br /&gt; ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF&lt;br /&gt; CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION&lt;br /&gt; WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.&lt;br /&gt; &lt;br /&gt; *)&lt;/em&gt;&lt;/span&gt;&lt;br /&gt; &lt;br /&gt; &lt;br /&gt; &lt;span style="color:#8d8d8d;"&gt;&lt;em&gt;-- Environment management&lt;br /&gt; &lt;/em&gt;&lt;/span&gt;&lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;on&lt;/strong&gt;&lt;/span&gt; modeEnvironment()&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;join &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;of&lt;/strong&gt;&lt;/span&gt; {&amp;quot;export __CF_USER_TEXT_ENCODING=0x1F5:0x8000100:0x8000100;&amp;quot;, &amp;quot;export SEE_MODE_RESOURCES=&amp;quot;, doubleQuotedForm for modeResources(), &amp;quot;; &amp;quot;, readEnvironment out &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;of&lt;/strong&gt;&lt;/span&gt; environmentFilePath()} by &amp;quot;&amp;quot;&lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;end&lt;/strong&gt;&lt;/span&gt; modeEnvironment&lt;br /&gt; &lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;on&lt;/strong&gt;&lt;/span&gt; modeSetting for envVar&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;try&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;tell&lt;/strong&gt;&lt;/span&gt; &lt;span style="color:#0b0bff;"&gt;application&lt;/span&gt; &amp;quot;System Events&amp;quot;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;tell&lt;/strong&gt;&lt;/span&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;property&lt;/strong&gt;&lt;/span&gt; list file (&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;my&lt;/strong&gt;&lt;/span&gt; environmentFilePath())&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;get&lt;/span&gt; value &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;of&lt;/strong&gt;&lt;/span&gt; the &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;property&lt;/strong&gt;&lt;/span&gt; list item envVar&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;end&lt;/strong&gt;&lt;/span&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;tell&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;end&lt;/strong&gt;&lt;/span&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;tell&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;on&lt;/strong&gt;&lt;/span&gt; &lt;span style="color:#0b0bff;"&gt;error&lt;/span&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;missing value&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;end&lt;/strong&gt;&lt;/span&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;try&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;end&lt;/strong&gt;&lt;/span&gt; modeEnvironmentSetting&lt;br /&gt; &lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;to&lt;/strong&gt;&lt;/span&gt; openEnvironmentSettings()&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;openEnvironment at environmentFilePath() &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;with&lt;/strong&gt;&lt;/span&gt; settingDefaultEnvironment&lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;end&lt;/strong&gt;&lt;/span&gt; openEnvironment&lt;br /&gt; &lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;on&lt;/strong&gt;&lt;/span&gt; environmentFilePath()&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;tell&lt;/strong&gt;&lt;/span&gt; &lt;span style="color:#0b0bff;"&gt;application&lt;/span&gt; &amp;quot;SubEthaEdit&amp;quot; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;to&lt;/strong&gt;&lt;/span&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;set&lt;/strong&gt;&lt;/span&gt; modeName &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;to&lt;/strong&gt;&lt;/span&gt; name &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;of&lt;/strong&gt;&lt;/span&gt; the mode &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;of&lt;/strong&gt;&lt;/span&gt; the &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;front&lt;/strong&gt;&lt;/span&gt; document&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;join &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;of&lt;/strong&gt;&lt;/span&gt; {path &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;to&lt;/strong&gt;&lt;/span&gt; preferences from user domain &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;as&lt;/strong&gt;&lt;/span&gt; string, &amp;quot;de.codingmonkeys.SubEthaEdit.&amp;quot;, modeName, &amp;quot;_environment.plist&amp;quot;} by &amp;quot;&amp;quot;&lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;end&lt;/strong&gt;&lt;/span&gt; environmentFileName&lt;br /&gt; &lt;br /&gt; &lt;span style="color:#8d8d8d;"&gt;&lt;em&gt;-- Manipulation of document text&lt;br /&gt; &lt;/em&gt;&lt;/span&gt;&lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;on&lt;/strong&gt;&lt;/span&gt; documentText()&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;tell&lt;/strong&gt;&lt;/span&gt; the &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;front&lt;/strong&gt;&lt;/span&gt; document &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;of&lt;/strong&gt;&lt;/span&gt; &lt;span style="color:#0b0bff;"&gt;application&lt;/span&gt; &amp;quot;SubEthaEdit&amp;quot; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;to&lt;/strong&gt;&lt;/span&gt; &lt;span style="color:#0b0bff;"&gt;get&lt;/span&gt; the contents&lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;end&lt;/strong&gt;&lt;/span&gt; documentText&lt;br /&gt; &lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;on&lt;/strong&gt;&lt;/span&gt; selectionIsEmpty()&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;tell&lt;/strong&gt;&lt;/span&gt; the &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;front&lt;/strong&gt;&lt;/span&gt; document &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;of&lt;/strong&gt;&lt;/span&gt; &lt;span style="color:#0b0bff;"&gt;application&lt;/span&gt; &amp;quot;SubEthaEdit&amp;quot; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;to&lt;/strong&gt;&lt;/span&gt; &lt;span style="color:#0b0bff;"&gt;get&lt;/span&gt; the length &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;of&lt;/strong&gt;&lt;/span&gt; the selection&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;result&lt;/span&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;is&lt;/strong&gt;&lt;/span&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;equal&lt;/strong&gt;&lt;/span&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;to&lt;/strong&gt;&lt;/span&gt; 0&lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;end&lt;/strong&gt;&lt;/span&gt; selectionIsEmpty&lt;br /&gt; &lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;to&lt;/strong&gt;&lt;/span&gt; completeSelectedLines()&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;extendSelection &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;with&lt;/strong&gt;&lt;/span&gt; extendingFront &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;and&lt;/strong&gt;&lt;/span&gt; extendingEnd&lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;end&lt;/strong&gt;&lt;/span&gt; completeSelectedLines&lt;br /&gt; &lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;on&lt;/strong&gt;&lt;/span&gt; selectionText()&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;tell&lt;/strong&gt;&lt;/span&gt; the &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;front&lt;/strong&gt;&lt;/span&gt; document &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;of&lt;/strong&gt;&lt;/span&gt; &lt;span style="color:#0b0bff;"&gt;application&lt;/span&gt; &amp;quot;SubEthaEdit&amp;quot; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;to&lt;/strong&gt;&lt;/span&gt; &lt;span style="color:#0b0bff;"&gt;get&lt;/span&gt; the contents &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;of&lt;/strong&gt;&lt;/span&gt; the selection&lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;end&lt;/strong&gt;&lt;/span&gt; selectionText&lt;br /&gt; &lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;to&lt;/strong&gt;&lt;/span&gt; setSelectionText &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;to&lt;/strong&gt;&lt;/span&gt; newText&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;tell&lt;/strong&gt;&lt;/span&gt; &lt;span style="color:#0b0bff;"&gt;application&lt;/span&gt; &amp;quot;SubEthaEdit&amp;quot; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;to&lt;/strong&gt;&lt;/span&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;set&lt;/strong&gt;&lt;/span&gt; the contents &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;of&lt;/strong&gt;&lt;/span&gt; the selection &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;of&lt;/strong&gt;&lt;/span&gt; the &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;front&lt;/strong&gt;&lt;/span&gt; document &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;to&lt;/strong&gt;&lt;/span&gt; the newText&lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;end&lt;/strong&gt;&lt;/span&gt; setSelectionText&lt;br /&gt; &lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;on&lt;/strong&gt;&lt;/span&gt; selectionRange &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;given&lt;/strong&gt;&lt;/span&gt; extendingFront:shouldExtendFront, extendingEnd:shouldExtendEnd&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;tell&lt;/strong&gt;&lt;/span&gt; the &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;front&lt;/strong&gt;&lt;/span&gt; document &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;of&lt;/strong&gt;&lt;/span&gt; &lt;span style="color:#0b0bff;"&gt;application&lt;/span&gt; &amp;quot;SubEthaEdit&amp;quot;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;if&lt;/strong&gt;&lt;/span&gt; shouldExtendFront &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;and&lt;/strong&gt;&lt;/span&gt; shouldExtendEnd &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;then&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;get&lt;/span&gt; {startCharacterIndex &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;of&lt;/strong&gt;&lt;/span&gt; the &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;first&lt;/strong&gt;&lt;/span&gt; paragraph &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;of&lt;/strong&gt;&lt;/span&gt; the selection, nextCharacterIndex &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;of&lt;/strong&gt;&lt;/span&gt; the &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;last&lt;/strong&gt;&lt;/span&gt; paragraph &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;of&lt;/strong&gt;&lt;/span&gt; the selection}&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;else&lt;/strong&gt;&lt;/span&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;if&lt;/strong&gt;&lt;/span&gt; shouldExtendFront &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;then&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;get&lt;/span&gt; {startCharacterIndex &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;of&lt;/strong&gt;&lt;/span&gt; the &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;first&lt;/strong&gt;&lt;/span&gt; paragraph &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;of&lt;/strong&gt;&lt;/span&gt; the selection, nextCharacterIndex &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;of&lt;/strong&gt;&lt;/span&gt; the selection}&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;else&lt;/strong&gt;&lt;/span&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;if&lt;/strong&gt;&lt;/span&gt; shouldExtendEnd &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;then&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;get&lt;/span&gt; {startCharacterIndex &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;of&lt;/strong&gt;&lt;/span&gt; the selection, nextCharacterIndex &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;of&lt;/strong&gt;&lt;/span&gt; the &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;last&lt;/strong&gt;&lt;/span&gt; paragraph &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;of&lt;/strong&gt;&lt;/span&gt; the selection}&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;else&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;get&lt;/span&gt; {startCharacterIndex &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;of&lt;/strong&gt;&lt;/span&gt; the selection, nextCharacterIndex &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;of&lt;/strong&gt;&lt;/span&gt; the selection}&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;end&lt;/strong&gt;&lt;/span&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;if&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;end&lt;/strong&gt;&lt;/span&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;tell&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;end&lt;/strong&gt;&lt;/span&gt; selectionRange&lt;br /&gt; &lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;to&lt;/strong&gt;&lt;/span&gt; setSelectionRange &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;to&lt;/strong&gt;&lt;/span&gt; newRange&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;tell&lt;/strong&gt;&lt;/span&gt; the &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;front&lt;/strong&gt;&lt;/span&gt; document &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;of&lt;/strong&gt;&lt;/span&gt; &lt;span style="color:#0b0bff;"&gt;application&lt;/span&gt; &amp;quot;SubEthaEdit&amp;quot;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;set&lt;/strong&gt;&lt;/span&gt; selection &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;to&lt;/strong&gt;&lt;/span&gt; newRange&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;end&lt;/strong&gt;&lt;/span&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;tell&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;end&lt;/strong&gt;&lt;/span&gt; setSelectionRange&lt;br /&gt; &lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;on&lt;/strong&gt;&lt;/span&gt; extendedSelectionText &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;given&lt;/strong&gt;&lt;/span&gt; extendingFront:shouldExtendFront, extendingEnd:shouldExtendEnd&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;set&lt;/strong&gt;&lt;/span&gt; {startChar, nextChar} &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;to&lt;/strong&gt;&lt;/span&gt; selectionRange &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;given&lt;/strong&gt;&lt;/span&gt; extendingFront:shouldExtendFront, extendingEnd:shouldExtendEnd&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;tell&lt;/strong&gt;&lt;/span&gt; the &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;front&lt;/strong&gt;&lt;/span&gt; document &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;of&lt;/strong&gt;&lt;/span&gt; &lt;span style="color:#0b0bff;"&gt;application&lt;/span&gt; &amp;quot;SubEthaEdit&amp;quot;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;get&lt;/span&gt; the contents &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;of&lt;/strong&gt;&lt;/span&gt; characters startChar through (nextChar - 1) &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;as&lt;/strong&gt;&lt;/span&gt; text&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;end&lt;/strong&gt;&lt;/span&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;tell&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;end&lt;/strong&gt;&lt;/span&gt; extendedSelectionText&lt;br /&gt; &lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;to&lt;/strong&gt;&lt;/span&gt; extendSelection &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;given&lt;/strong&gt;&lt;/span&gt; extendingFront:shouldExtendFront, extendingEnd:shouldExtendEnd&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;set&lt;/strong&gt;&lt;/span&gt; {startChar, nextChar} &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;to&lt;/strong&gt;&lt;/span&gt; (selectionRange &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;given&lt;/strong&gt;&lt;/span&gt; extendingFront:shouldExtendFront, extendingEnd:shouldExtendEnd)&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;setSelectionRange &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;to&lt;/strong&gt;&lt;/span&gt; {startChar, nextChar-1}&lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;end&lt;/strong&gt;&lt;/span&gt; extendSelection&lt;br /&gt; &lt;br /&gt; &lt;span style="color:#8d8d8d;"&gt;&lt;em&gt;-- Manipulation of document properties&lt;br /&gt; &lt;/em&gt;&lt;/span&gt;&lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;to&lt;/strong&gt;&lt;/span&gt; checkSaveStatus &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;given&lt;/strong&gt;&lt;/span&gt; updating:shouldSave&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;tell&lt;/strong&gt;&lt;/span&gt; &lt;span style="color:#0b0bff;"&gt;application&lt;/span&gt; &amp;quot;SubEthaEdit&amp;quot;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;if&lt;/strong&gt;&lt;/span&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;not&lt;/strong&gt;&lt;/span&gt; (&lt;span style="color:#0b0bff;"&gt;exists&lt;/span&gt; path &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;of&lt;/strong&gt;&lt;/span&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;front&lt;/strong&gt;&lt;/span&gt; document) &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;then&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;error&lt;/span&gt; &amp;quot;You have to save the document first&amp;quot;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;end&lt;/strong&gt;&lt;/span&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;if&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;if&lt;/strong&gt;&lt;/span&gt; shouldSave &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;and&lt;/strong&gt;&lt;/span&gt; (modified &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;of&lt;/strong&gt;&lt;/span&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;front&lt;/strong&gt;&lt;/span&gt; document) &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;then&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;try&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;save&lt;/span&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;front&lt;/strong&gt;&lt;/span&gt; document&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;end&lt;/strong&gt;&lt;/span&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;try&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;end&lt;/strong&gt;&lt;/span&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;if&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;end&lt;/strong&gt;&lt;/span&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;tell&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;end&lt;/strong&gt;&lt;/span&gt; checkSaveStatus&lt;br /&gt; &lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;on&lt;/strong&gt;&lt;/span&gt; documentIsAvailable()&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;tell&lt;/strong&gt;&lt;/span&gt; &lt;span style="color:#0b0bff;"&gt;application&lt;/span&gt; &amp;quot;SubEthaEdit&amp;quot; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;to&lt;/strong&gt;&lt;/span&gt; &lt;span style="color:#0b0bff;"&gt;get&lt;/span&gt; the &lt;span style="color:#0b0bff;"&gt;count&lt;/span&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;of&lt;/strong&gt;&lt;/span&gt; the documents&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;result&lt;/span&gt; &amp;gt; 0&lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;end&lt;/strong&gt;&lt;/span&gt; documentIsAvailable&lt;br /&gt; &lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;to&lt;/strong&gt;&lt;/span&gt; requireNewlineAtEOF()&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;tell&lt;/strong&gt;&lt;/span&gt; the &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;front&lt;/strong&gt;&lt;/span&gt; document &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;of&lt;/strong&gt;&lt;/span&gt; &lt;span style="color:#0b0bff;"&gt;application&lt;/span&gt; &amp;quot;SubEthaEdit&amp;quot;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;if&lt;/strong&gt;&lt;/span&gt; &amp;quot;&amp;quot; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;is&lt;/strong&gt;&lt;/span&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;equal&lt;/strong&gt;&lt;/span&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;to&lt;/strong&gt;&lt;/span&gt; the contents &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;of&lt;/strong&gt;&lt;/span&gt; the &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;last&lt;/strong&gt;&lt;/span&gt; paragraph &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;then&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#8d8d8d;"&gt;&lt;em&gt;-- final line terminated, do nothing&lt;br /&gt; &lt;/em&gt;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;else&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;set&lt;/strong&gt;&lt;/span&gt; the contents &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;of&lt;/strong&gt;&lt;/span&gt; the &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;last&lt;/strong&gt;&lt;/span&gt; insertion point &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;of&lt;/strong&gt;&lt;/span&gt; the &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;last&lt;/strong&gt;&lt;/span&gt; paragraph &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;to&lt;/strong&gt;&lt;/span&gt; &lt;span style="color:#0b0bff;"&gt;return&lt;/span&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;end&lt;/strong&gt;&lt;/span&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;if&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;end&lt;/strong&gt;&lt;/span&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;tell&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;end&lt;/strong&gt;&lt;/span&gt; requireNewlineAtEOF&lt;br /&gt; &lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;on&lt;/strong&gt;&lt;/span&gt; documentPath()&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;tell&lt;/strong&gt;&lt;/span&gt; &lt;span style="color:#0b0bff;"&gt;application&lt;/span&gt; &amp;quot;SubEthaEdit&amp;quot; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;to&lt;/strong&gt;&lt;/span&gt; &lt;span style="color:#0b0bff;"&gt;get&lt;/span&gt; the path &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;of&lt;/strong&gt;&lt;/span&gt; the &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;front&lt;/strong&gt;&lt;/span&gt; document&lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;end&lt;/strong&gt;&lt;/span&gt; documentPath&lt;br /&gt; &lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;on&lt;/strong&gt;&lt;/span&gt; documentLine()&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;tell&lt;/strong&gt;&lt;/span&gt; &lt;span style="color:#0b0bff;"&gt;application&lt;/span&gt; &amp;quot;SubEthaEdit&amp;quot; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;to&lt;/strong&gt;&lt;/span&gt; &lt;span style="color:#0b0bff;"&gt;get&lt;/span&gt; the startLineNumber &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;of&lt;/strong&gt;&lt;/span&gt; selection &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;of&lt;/strong&gt;&lt;/span&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;front&lt;/strong&gt;&lt;/span&gt; document&lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;end&lt;/strong&gt;&lt;/span&gt; documentLine&lt;br /&gt; &lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;on&lt;/strong&gt;&lt;/span&gt; modeResources()&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;tell&lt;/strong&gt;&lt;/span&gt; &lt;span style="color:#0b0bff;"&gt;application&lt;/span&gt; &amp;quot;SubEthaEdit&amp;quot; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;to&lt;/strong&gt;&lt;/span&gt; &lt;span style="color:#0b0bff;"&gt;get&lt;/span&gt; the resource path &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;of&lt;/strong&gt;&lt;/span&gt; the mode &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;of&lt;/strong&gt;&lt;/span&gt; the &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;front&lt;/strong&gt;&lt;/span&gt; document&lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;end&lt;/strong&gt;&lt;/span&gt; modeResources&lt;br /&gt; &lt;br /&gt; &lt;span style="color:#8d8d8d;"&gt;&lt;em&gt;-- String Utilities&lt;br /&gt; &lt;/em&gt;&lt;/span&gt;&lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;on&lt;/strong&gt;&lt;/span&gt; replacement &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;of&lt;/strong&gt;&lt;/span&gt; oldDelim by newDelim for sourceString&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;return&lt;/span&gt; join &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;of&lt;/strong&gt;&lt;/span&gt; (tokens &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;of&lt;/strong&gt;&lt;/span&gt; sourceString between oldDelim) by newDelim&lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;end&lt;/strong&gt;&lt;/span&gt; replacement&lt;br /&gt; &lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;on&lt;/strong&gt;&lt;/span&gt; tokens &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;of&lt;/strong&gt;&lt;/span&gt; str between delimiters&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;set&lt;/strong&gt;&lt;/span&gt; oldTIDs &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;to&lt;/strong&gt;&lt;/span&gt; text item delimiters &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;of&lt;/strong&gt;&lt;/span&gt; AppleScript&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;set&lt;/strong&gt;&lt;/span&gt; text item delimiters &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;of&lt;/strong&gt;&lt;/span&gt; AppleScript &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;to&lt;/strong&gt;&lt;/span&gt; delimiters&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;set&lt;/strong&gt;&lt;/span&gt; strtoks &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;to&lt;/strong&gt;&lt;/span&gt; text items &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;of&lt;/strong&gt;&lt;/span&gt; str&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;set&lt;/strong&gt;&lt;/span&gt; text item delimiters &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;of&lt;/strong&gt;&lt;/span&gt; AppleScript &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;to&lt;/strong&gt;&lt;/span&gt; oldTIDs&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;return&lt;/span&gt; strtoks&lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;end&lt;/strong&gt;&lt;/span&gt; tokens&lt;br /&gt; &lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;on&lt;/strong&gt;&lt;/span&gt; join &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;of&lt;/strong&gt;&lt;/span&gt; tokenList by delimiter&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;set&lt;/strong&gt;&lt;/span&gt; oldTIDs &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;to&lt;/strong&gt;&lt;/span&gt; text item delimiters &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;of&lt;/strong&gt;&lt;/span&gt; AppleScript&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;set&lt;/strong&gt;&lt;/span&gt; text item delimiters &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;of&lt;/strong&gt;&lt;/span&gt; AppleScript &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;to&lt;/strong&gt;&lt;/span&gt; delimiter&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;set&lt;/strong&gt;&lt;/span&gt; joinedString &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;to&lt;/strong&gt;&lt;/span&gt; tokenList &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;as&lt;/strong&gt;&lt;/span&gt; string&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;set&lt;/strong&gt;&lt;/span&gt; text item delimiters &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;of&lt;/strong&gt;&lt;/span&gt; AppleScript &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;to&lt;/strong&gt;&lt;/span&gt; oldTIDs&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;return&lt;/span&gt; joinedString&lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;end&lt;/strong&gt;&lt;/span&gt; join&lt;br /&gt; &lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;on&lt;/strong&gt;&lt;/span&gt; doubleQuotedForm for baseString &amp;nbsp;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;quote &amp;amp; baseString &amp;amp; quote&lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;end&lt;/strong&gt;&lt;/span&gt; doubleQuotedForm&lt;br /&gt; &lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;on&lt;/strong&gt;&lt;/span&gt; shellTransform &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;of&lt;/strong&gt;&lt;/span&gt; inText for envString through pipeline &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;given&lt;/strong&gt;&lt;/span&gt; alteringLineEndings:altEnds&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;set&lt;/strong&gt;&lt;/span&gt; shellscript &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;to&lt;/strong&gt;&lt;/span&gt; join &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;of&lt;/strong&gt;&lt;/span&gt; {envString, &amp;quot;pbpaste&amp;quot;, &amp;quot;|&amp;quot;, pipeline} by &lt;span style="color:#0b0bff;"&gt;space&lt;/span&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;set&lt;/strong&gt;&lt;/span&gt; the oldClipboard &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;to&lt;/strong&gt;&lt;/span&gt; the clipboard&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;set&lt;/strong&gt;&lt;/span&gt; the clipboard &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;to&lt;/strong&gt;&lt;/span&gt; the inText&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;try&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;set&lt;/strong&gt;&lt;/span&gt; shellresponse &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;to&lt;/strong&gt;&lt;/span&gt; do shell script shellscript altering line endings altEnds&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;on&lt;/strong&gt;&lt;/span&gt; &lt;span style="color:#0b0bff;"&gt;error&lt;/span&gt; errMsg number errNum from badObject&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;set&lt;/strong&gt;&lt;/span&gt; the clipboard &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;to&lt;/strong&gt;&lt;/span&gt; the oldClipboard&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;error&lt;/span&gt; errMsg number errNum from badObject&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;end&lt;/strong&gt;&lt;/span&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;try&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;set&lt;/strong&gt;&lt;/span&gt; the clipboard &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;to&lt;/strong&gt;&lt;/span&gt; the oldClipboard&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;shellresponse&lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;end&lt;/strong&gt;&lt;/span&gt; shellTransform&lt;br /&gt; &lt;br /&gt; &lt;span style="color:#8d8d8d;"&gt;&lt;em&gt;-- Handling of environment settings using a plist file&lt;br /&gt; &lt;/em&gt;&lt;/span&gt;&lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;to&lt;/strong&gt;&lt;/span&gt; writeDefaultEnvironment at envPath&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;set&lt;/strong&gt;&lt;/span&gt; savedClipboard &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;to&lt;/strong&gt;&lt;/span&gt; the clipboard&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;set&lt;/strong&gt;&lt;/span&gt; the clipboard &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;to&lt;/strong&gt;&lt;/span&gt; &amp;quot;&amp;lt;?xml version=\&amp;quot;1.0\&amp;quot; encoding=\&amp;quot;UTF-8\&amp;quot;?&amp;gt;&lt;br /&gt; &amp;lt;!DOCTYPE plist PUBLIC \&amp;quot;-//Apple Computer//DTD PLIST 1.0//EN\&amp;quot; \&amp;quot;http://www.apple.com/DTDs/PropertyList-1.0.dtd\&amp;quot;&amp;gt;&lt;br /&gt; &amp;lt;plist version=\&amp;quot;1.0\&amp;quot;&amp;gt;&lt;br /&gt; &amp;lt;dict/&amp;gt;&lt;br /&gt; &amp;lt;/plist&amp;gt;&amp;quot;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;try&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;do shell script &amp;quot;pbpaste &amp;gt; &amp;quot; &amp;amp; (POSIX path &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;of&lt;/strong&gt;&lt;/span&gt; envPath)&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;on&lt;/strong&gt;&lt;/span&gt; &lt;span style="color:#0b0bff;"&gt;error&lt;/span&gt; errMsg number errNum from badObject&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;set&lt;/strong&gt;&lt;/span&gt; the clipboard &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;to&lt;/strong&gt;&lt;/span&gt; the savedClipboard&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;error&lt;/span&gt; errMsg number errNum from badObject&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;end&lt;/strong&gt;&lt;/span&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;try&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;set&lt;/strong&gt;&lt;/span&gt; the clipboard &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;to&lt;/strong&gt;&lt;/span&gt; the savedClipboard&lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;end&lt;/strong&gt;&lt;/span&gt; writeDefaultEnvironment&lt;br /&gt; &lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;to&lt;/strong&gt;&lt;/span&gt; openEnvironment at envFilePath &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;given&lt;/strong&gt;&lt;/span&gt; settingDefaultEnvironment:shouldSetDefault&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;tell&lt;/strong&gt;&lt;/span&gt; &lt;span style="color:#0b0bff;"&gt;application&lt;/span&gt; &amp;quot;System Events&amp;quot;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;if&lt;/strong&gt;&lt;/span&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;not&lt;/strong&gt;&lt;/span&gt; &lt;span style="color:#0b0bff;"&gt;exists&lt;/span&gt; file envFilePath&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;if&lt;/strong&gt;&lt;/span&gt; shouldSetDefault&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;my&lt;/strong&gt;&lt;/span&gt; writeDefaultEnvironment at envFilePath&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;else&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;error&lt;/span&gt; (&amp;quot;Can't get environment file &amp;quot; &amp;amp; quote &amp;amp; envFilePath &amp;amp; quote) number -1728&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;end&lt;/strong&gt;&lt;/span&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;if&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;end&lt;/strong&gt;&lt;/span&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;if&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;open&lt;/span&gt; file envFilePath&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;end&lt;/strong&gt;&lt;/span&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;tell&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;end&lt;/strong&gt;&lt;/span&gt; openEnvironment&lt;br /&gt; &lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;to&lt;/strong&gt;&lt;/span&gt; readEnvironment out &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;of&lt;/strong&gt;&lt;/span&gt; plist&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;readListPair out &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;of&lt;/strong&gt;&lt;/span&gt; plist&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;environmentString from &lt;span style="color:#0b0bff;"&gt;result&lt;/span&gt;&lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;end&lt;/strong&gt;&lt;/span&gt; readEnvironment&lt;br /&gt; &lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;to&lt;/strong&gt;&lt;/span&gt; readListPair out &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;of&lt;/strong&gt;&lt;/span&gt; plist&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;tell&lt;/strong&gt;&lt;/span&gt; &lt;span style="color:#0b0bff;"&gt;application&lt;/span&gt; &amp;quot;System Events&amp;quot;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;if&lt;/strong&gt;&lt;/span&gt; &lt;span style="color:#0b0bff;"&gt;exists&lt;/span&gt; file plist &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;then&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;tell&lt;/strong&gt;&lt;/span&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;property&lt;/strong&gt;&lt;/span&gt; list file plist&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;get&lt;/span&gt; {name, value} &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;of&lt;/strong&gt;&lt;/span&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;every&lt;/strong&gt;&lt;/span&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;property&lt;/strong&gt;&lt;/span&gt; list item&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;end&lt;/strong&gt;&lt;/span&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;tell&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;else&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{{}, {}}&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;end&lt;/strong&gt;&lt;/span&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;if&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;end&lt;/strong&gt;&lt;/span&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;tell&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;end&lt;/strong&gt;&lt;/span&gt; readPlist&lt;br /&gt; &lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;on&lt;/strong&gt;&lt;/span&gt; environmentString from keyValueListPair&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;set&lt;/strong&gt;&lt;/span&gt; {plistKeys, plistValues} &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;to&lt;/strong&gt;&lt;/span&gt; keyValueListPair&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;set&lt;/strong&gt;&lt;/span&gt; accumulator &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;to&lt;/strong&gt;&lt;/span&gt; {}&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;set&lt;/strong&gt;&lt;/span&gt; oldTIDs &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;to&lt;/strong&gt;&lt;/span&gt; text item delimiters &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;of&lt;/strong&gt;&lt;/span&gt; AppleScript&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;set&lt;/strong&gt;&lt;/span&gt; text item delimiters &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;of&lt;/strong&gt;&lt;/span&gt; AppleScript &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;to&lt;/strong&gt;&lt;/span&gt; &amp;quot;&amp;quot;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;repeat&lt;/strong&gt;&lt;/span&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;with&lt;/strong&gt;&lt;/span&gt; i from 1 &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;to&lt;/strong&gt;&lt;/span&gt; number &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;of&lt;/strong&gt;&lt;/span&gt; items &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;in&lt;/strong&gt;&lt;/span&gt; plistKeys&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;set&lt;/strong&gt;&lt;/span&gt; tokens &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;to&lt;/strong&gt;&lt;/span&gt; {&amp;quot;export &amp;quot;, item i &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;of&lt;/strong&gt;&lt;/span&gt; plistKeys, &amp;quot;=&amp;quot;, item i &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;of&lt;/strong&gt;&lt;/span&gt; plistValues, &amp;quot;;&amp;quot;}&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;copy&lt;/strong&gt;&lt;/span&gt; (tokens &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;as&lt;/strong&gt;&lt;/span&gt; string) &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;to&lt;/strong&gt;&lt;/span&gt; the &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;end&lt;/strong&gt;&lt;/span&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;of&lt;/strong&gt;&lt;/span&gt; the accumulator&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;end&lt;/strong&gt;&lt;/span&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;repeat&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;set&lt;/strong&gt;&lt;/span&gt; AppleScript's text item delimiters &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;to&lt;/strong&gt;&lt;/span&gt; &lt;span style="color:#0b0bff;"&gt;space&lt;/span&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;set&lt;/strong&gt;&lt;/span&gt; envString &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;to&lt;/strong&gt;&lt;/span&gt; accumulator &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;as&lt;/strong&gt;&lt;/span&gt; string&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;set&lt;/strong&gt;&lt;/span&gt; AppleScript's text item delimiters &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;to&lt;/strong&gt;&lt;/span&gt; oldTIDs&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;envString&lt;br /&gt; &lt;span style="color:#0b0bff;"&gt;&lt;strong&gt;end&lt;/strong&gt;&lt;/span&gt; environmentString&lt;br /&gt; &lt;br /&gt; &lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3164056879958238384-5813706735439064883?l=appliedabstraction.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://appliedabstraction.blogspot.com/feeds/5813706735439064883/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3164056879958238384&amp;postID=5813706735439064883' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/5813706735439064883'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3164056879958238384/posts/default/5813706735439064883'/><link rel='alternate' type='text/html' href='http://appliedabstraction.blogspot.com/2008/01/seeing-latex-19-subethaedittools-or.html' title='SEEing LaTeX 19: SubEthaEditTools, or, Eating My Own Dogfood'/><author><name>Michael Barber</name><uri>http://www.blogger.com/profile/15713879666980145844</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3164056879958238384.post-71006404796033657</id><published>2008-01-04T05:15:00.001-08:00</published><updated>2008-01-04T05:23:18.605-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='m4'/><category scheme='http://www.blogger.com/atom/ns#' term='AppleScript'/><category scheme='http://www.blogger.com/atom/ns#' term='shell'/><category scheme='http://www.blogger.com/atom/ns#' term='Mac OS X'/><title type='text'>What Role Should the Script Editor Play in Writing AppleScript?  </title><content type='html'>I think it is safe to say that Script Editor is the most widely used application for writing AppleScripts. But, should it be? I'm coming to the conclusion that it brings so many limitations along with it that it can only play a limited role. &lt;br /&gt;&lt;br /&gt;Before going any further with this line of thought, I'd like to make clear that I'm not making any recommendation for or against the various commercial AppleScript development environments. Looking at the &lt;a href="http://appliedabstraction.blogspot.com/search/label/SEEing%20LaTeX"&gt;SEEing LaTeX&lt;/a&gt; series, you might get the impression that I use AppleScript a fair amount. I don't. I use it as little as possible, which is why in that series I do most of the real work not in AppleScript, but in shell scripts called from AppleScript. Paying for a commercial environment may make sense if you want to or have to write a lot of AppleScript, but would be a waste of money for me. &lt;br /&gt;&lt;br /&gt;However, our choices are not limited to Script Editor and the commercial development environments. We have an excellent development environment that comes for free with Mac OS X: it's called Unix. We can use &lt;code&gt;make&lt;/code&gt; to manage a project, of which AppleScript 
