Increasing Productivity with Textmate's New tm_dialog

—Friday, November 03 2006

TextMate has been one of those tools that has significantly increased my productivity. Allan has managed to create an editor that is great for Designers and Unix geeks alike, and not to mention a vibrant and helpful community who seem to thrive on making contributions. Allan has recently upped the ante with the recently released tm_dialog; a new dialog system for commands. I’ve already come up with a use for it that helps me get my job done faster. In addition to that, I’ve made use of the Drag Command which is also helping me clean up my workflow.

Get to know tm_dialog

If you have yet to check out Allan’s screencast on tm_dialog, I encourage you to do so. He shows how powerful it is by using Interface Builder to create a dialog for viewing subversion logs; Something I do regularly. In addition to that, he’ll be creating a complete series that helps us make the best use of tm_dialog.

Cleaning up the mundane task

While editing CSS files, I find myself clicking through the project drawer looking for image file names and locations time and time again. When I do find the image I’m looking for, I end up repeatedly writing code like this:

#header {
   background: url(../images/hdr-bg.gif) no-repeat;
}

Now Textmate offers a variety of ways to keep you from having to manually type this out. You could create a snippet that triggered when you typed bg that would do most of the typing for you, but you still have to sort out the path or what not. Using a drag command, we can speed up this process.

Using Drag Commands

What I want to do here is to drag an image file from the project drawer and drop it onto the document and have TextMate type all the url stuff for me and figure out the relative path to the image. Thats great and all, but what about the no-repeat, repeat-x, etc.? Again, there are a number of ways to do this as well, but if your like me I usually have a common naming convention I use for tiling images. If an image tiles, I usually name it something like hdr-bg-tile.gif, or if it tiles on the x axis, I name it something like hdr-bg-xtile.gif. The same applies for the y tile.

Wouldn’t it be great if TextMate could figure out all this for us and the only thing we needed to do was just drag the image onto the document? Well, it can, and here is a short video of this in action:

Nice eh? If you want this behavior, just create a new drag command and pop in the code below. Set your file types (gif, png, jpg, etc) and scope to source.css.

#!/usr/bin/env ruby
image = ENV['TM_DROPPED_FILE']
image_name = File.basename(image)
repeat = ' no-repeat'

if /(xtile|tilex|ytile|tiley|tile)\./ =~ image_name
  unless $1.nil?
    repeat = case $1
      when 'xtile', 'tilex' then ' repeat-x'
      when 'ytile', 'tiley' then ' repeat-y'
      when 'tile'           then ''
    end
  end
end

print %(url(#{image})#{repeat})

Using tm_dialog to create a menu with project images

One of the cool features of tm_dialog is that we now have the ability to create custom menus. With this in mind, I wanted to stop the hunt and peck process–Hunting for images in the project drawer, and typing the required information to make that image display in css. I want to hit a key combination and have TextMate present me with a menu of images it found in the project directory, when I select an image from the menu, it should fill in all the required info. And here it is:

I used Allan’s example for tm_dialog posted here and came up with this bit of Ruby: UPDATE: Modified to ignore .svn files.

#!/usr/bin/env ruby -wKU

SUPPORT = ENV['TM_SUPPORT_PATH']
DIALOG = SUPPORT + '/bin/tm_dialog'

require SUPPORT + '/lib/escape'
require SUPPORT + '/lib/plist'
require 'pathname'
require 'find'

images = []

Find.find(ENV['TM_PROJECT_DIRECTORY']) do |f|
  file_name = File.basename(f)
  if  /\.(gif|jpg|png)$/ =~ file_name
    images << { 
      'title' => file_name, 
      'path'  => Pathname.new(f).relative_path_from(Pathname.new(ENV['TM_FILEPATH']).parent).to_s 
    }
  end
end
abort if images.empty?

plist = { 'menuItems' => images }.to_plist
res = PropertyList::load(`#{e_sh DIALOG} -up #{e_sh plist}`)
abort unless res.has_key? 'selectedMenuItem'

print %(url(#{res['selectedMenuItem']['path']}))

This code uses Find and Pathname to get the images and their path and in turn stores those values in a Hash. When we have the hash built, we create a property list from it, using menuItems as the root key for the array of image information we just build.

After we have the plist, we can pass it on to the actual dialog in the next line. If the user selects an item from the menu the resource will have the key selectedMenuItem and then we’ll take that and ask for the path value which is wrapped up in the correct CSS syntax for referencing images and then printed to the document.

As you can see, this is some pretty nice stuff and TextMate makes it dead simple to increase your productivity and extend it into an editor that is perfect for you.

(P.S. Sorry for the video size, I’m experimenting with Revver, whom I’ve recently been doing some work for.)