RunVim

RunVim runs vim within Terminal when one double-clicks on a file mapped to it in Finder. While this document focuses on running vim, it is trivial to change the program to make it run whatever editor you see fit, e.g. emacs, pico, etc.

Files

[ Application: RunVim.zip | Xcode Project: RunVimProj.zip ]

Alternatives

Before I go into the details of how RunVim works, let's first consider a couple alternative methods to accomplish the same thing.

Alternative 1: Map File Types to gvim

You could use the Carbon-based gvim to accomplish this. The downside of this, is that if you do most of your work in Terminal, then gvim represents an additional application to deal with.

Alternative 2: Write a .command Script

You could use a .command script to launch vim in Terminal. The problem with this, is that applications do not receive any useful arguments when they are run by LaunchServices1. A command script cannot be used to open arbitrary files.

How It Works

Finder ~~~ LaunchServices ~~~ RunVim ~~~~~~~~~~~ Terminal ~~~~ Vim
   |          ^     |          ^  |               ^  |          ^
   +-- open --+     +-- open --+  +-- do script --+  +-- exec --+
The upper row depicts the applications (or libraries) at work. The lower row depicts the AppleEvent messages, sent from one node to another. The last message is actually a system call.
  1. The user double-clicks on a file, e.g. addr.txt
  2. Finder sends an open addr.txt request to LaunchServices.
  3. LaunchServices looks up the application to run for addr.txt in its database2.
  4. It launches RunVim and sends it an open addr.txt request.
  5. RunVim starts, and installs an on open handler, which is called immediately.
  6. The on open handler sends a do script command to Terminal.
  7. Terminal opens a new window for the request, and executes the command: cd dir; vim addr.txt; exit.
  8. vim starts, processes its command-line args, and opens the requested file.
While this may seem like a lot of work just to open a file, it is the Macintosh way. Steps 1 – 5 are typical of all open requests within Finder.

The Script

RunVim.applescript

The first handler is called when the app is launched directly. It tells Terminal to run vim and then shuts down the RunVim app.

-- Run command: "vim; exit"
on open untitled obj
  tell application "Terminal"
    do script "vim; exit"
    activate
  end
  quit application
end

The second handler is more involved. It takes the first alias in the list and splits it into directory pieces. It immediately joins them back together, omitting the last node (the filename) to obtain the parent directory. It then builds a list of file names obtained from the info dictionary of each alias. It cats these together, and finishes in the same manner as the first handler.

-- Run command: "cd '/posix/path'; vim -- 'f1' 'f2'; exit"
on open alias_list
  set a to item 1 of alias_list
  set text item delimiters to ":"
  set list1 to text items of (a as string)
  set dir to (items 1 thru -2 of list1) as string
  set dir to quoted form of POSIX path of dir
  set list1 to {}
  repeat with a in alias_list
    set n to quoted form of name of (info for a)
    set end of list1 to n
  end
  set text item delimiters to " "
  set cat to list1 as text
  tell application "Terminal"
    do script "cd " & dir & "; vim -- " & cat & "; exit"
    activate
  end
  quit application
end

Gotchas

There are a number of snags out there in the realm of AppleScript apps. Here's a quick rundown of the ones I hit:

ScriptEditor

ScriptEditor can produce Applets, Bundles, and Droplets. I found no way to coerce Finder into accepting a product of ScriptEditor as a full-on Application. In other words, a bundle that can have file types mapped to it by file extension.

InterfaceBuilder (.nib Files)

InterfaceBuilder has two purposes. First, it provides a way to draw GUIs. Second, it provides a way to bind message senders to sinks, i.e., to establish Objective-C bindings. To do a GUI-less AppleScript Studio application, you still have to spend some time in InterfaceBuilder to establish these bindings.

The relevant messages are found within the Files Owner instance. To bind them, click on the Files Owner icon, then press Shift-Cmd-I (Inspector), then Cmd-8 (AppleScript). Voilà.

Rosetta v. AppleScript Studio

If you're building on a PPC machine, note that Rosetta does not like the product of AppleScript Studio — it crashes. Be sure to generate a Universal Binary:
  Project > Edit Project Settings > Build > Architectures: ppc,i386

Resources

 • AppleScript Studio Programming Guide
 • AppleScript Language Guide
 • Common Scripting Tasks
 • Vim.org

Acknowledgements

Thanks to Jesus Lopez (blanka) for trying this out on narf2006's Mac Mini x86, without even asking.

Comments, Questions? Email: Jim Eberle.

1. LaunchServices runs programs with a single argument of -psn_d_dddd, the process serial number. You can see these with:

  $ ps -ax

2. You can inspect the file type bindings with lsregister:

  $ AS='ApplicationServices.framework/Versions/Current'
  $ LS='LaunchServices.framework/Versions/Current'
  $ alias lsreg="/System/Library/Frameworks/$AS/Frameworks/$LS/Support/lsregister"
  $ lsreg -dump | more
Copyright © 2010 Jim Eberle, All rights reserved.

The author assumes no liability relating to any action you take based on the contents or information on this site. No warranties of any kind.

This document is an independent publication and has not been authorized, sponsored, or otherwise approved by Apple Computer, Inc.

All trademarks and registered trademarks are the property of their respective owners.

Bundled icons, app.icns, doc.icns, doc-txt.icns, are from Vim.org.