Pretty image
Make your editor work for you—and work hard.

The people I work with are often surprised by how fast I crank out the tedious tasks. At the risk of ruining my image, I must confess that I’m really just a compulsive user of TextMate’s automation features. What continually surprises me is how easy it is to teach TextMate new tricks, customized to my personal tastes. I spend a little effort up front when embarking on some new project and TextMate pays me back over and over again as I do the real work.

For example, let’s say I decided it was time to branch into writing fiction. Most people would start by looking into all of the software available for authoring fiction, but I start by thinking about how I’m going to get TextMate ready for this new venture. I’m already invested in TextMate and it is now super painful for me to write anything without it. When typing an email into Apple’s Mail.app, I regularly highlight some text, type a quotation mark, and freak out when half of my message is replaced. Composing a larger work in anything dumber than TextMate would just be far too stressful for me.

I bet most of you are thinking that I’ve already lost the plot. I mean, let’s face it, aside from saving a simple text file, our output options kind of suck with TextMate. How are we going to compose something we wouldn’t be embarrassed to show to a publisher? We could resort to a hardcore geek tool like LaTeX, but then we might end up focusing more on the format than the content. Well, I say we’ve just found the first automation we are going to need. Let’s teach TextMate how to produce decent PDF files from our simple content.

I know, I know. Publishers aren’t clamoring for manuscripts in PDF format. I’m talking about producing something professional-looking for the elevator pitch. And, frankly, something that makes me feel like this prose is really publishable.

A Novel Approach

Let’s assume I have my novel project started, and that it looks, at this point, something like this screen shot:

project.jpg

What I need is a command that can read through the chapters and transform them into a pretty PDF. Let’s make that happen.

We’ll surely need more than one command, though, so let’s build a bundle, TextMate lingo for a group of related automations, for these fiction commands. In the Bundles menu I’m going to choose Bundle Editor and then Show Bundle Editor. Then I’m going to click and hold the plus button in the lower left corner of that window and select New Bundle. After that, I can just type in the name of the bundle I am creating, which is Fictional Work in this case.

Now that I have a place to hold commands, I’m ready to write one. Of course, I have no desire to build a PDF manually. It’s time for a quick cheat. I’m going to do my scripting with my favorite scripting weapon, Ruby, which has a powerful PDF generating library called Prawn. We need to make sure we have a recent version of that before we ask it to do the heavy lifting for us, so I’m going to bounce over to the Terminal and run the install command:

 sudo gem install prawn

Now let’s head back to TextMate and the Bundle Editor so we can build a command. First I’m going to click on the Fiction bundle to make sure it’s highlighted, then it’s back to the plus button in the lower left, and this time I’ll select New Command. I’ll name this one Build Fictional PDF. I also need to initialize a few settings for this command. First, I’ll flip Input to None since the command will just read everything from the chapters directory. Output needs to be Show as HTML, because that’s the easiest way to stream some progress information to the user as we work.

We’re now ready to write the actual code. Remember how I said I can’t really type outside of TextMate’s main editing window though? Well, that includes the Bundle Editor. I’m just going to click my cursor into the Command(s) box and then go into the Edit menu after the Edit in TextMate command. If you don’t have that one installed, just ask TextMate to add it for you by opening the Bundles menu and selecting TextMate and then Install "Edit in TextMate".

We’re finally ready to write some Ruby:

 #!/usr/bin/env ruby -KU
 
 require "pathname"
 require "fileutils"
 require "yaml"
 require "erb"
 
 require "#{ENV['TM_SUPPORT_PATH']}/lib/textmate"
 require "#{ENV['TM_SUPPORT_PATH']}/lib/web_preview"
 
 require "rubygems"
 require "prawn"
 require "prawn/format"
 
 # Figure out paths, title, and user's name
 PROJ_DIR = Pathname.new(ENV["TM_PROJECT_DIRECTORY"])
 CHAPTERS_DIR = PROJ_DIR + "chapters"
 PDFS_DIR = PROJ_DIR + "pdfs"
 FILE = PDFS_DIR +
  "#{Time.now.strftime('%Y-%m-%d-%H-%M-%S')}.pdf"
 TITLE = PROJ_DIR.basename.to_s
 USER = %x{dscl . read /Users/$USER realname}.
  sub(/\A\S*realname:\s*/, "")
 # Configure options
 options = (yaml = PROJ_DIR + "config" + "pdf.yaml").exist? ?
  YAML.load(yaml.read) : { }
 margins = { :top_margin => options.fetch("top_margin", 72),
  :left_margin => options.fetch("left_margin", 72),
  :right_margin => options.fetch("right_margin", 72),
  :bottom_margin => options.fetch("bottom_margin", 72) }
 font_size = {:size => options.fetch("font_size", 10)}
 font_size[:spacing] = options.fetch("spacing", font_size[:size] * 1.5)
 header_font_size = { :align => :center,
  :size => font_size[:size] * 2,
  :spacing => font_size[:spacing] * 2 }
 
 # Build the PDF
 html_header("Fictional PDF Builder")
 puts "<ol>"
 Prawn::Document.generate(FILE, :skip_page_creation => true) do
  start_new_page(margins.merge(:top_margin => 360))
  font(options.fetch("font", "Courier"))
  text("<b>#{TITLE}</b><br/>by #{USER}", header_font_size)
  CHAPTERS_DIR.each_entry do |chapter|
  if (chapter = CHAPTERS_DIR + chapter).file?
  puts "<li>Processing #{chapter.basename}</li>"
  start_new_page(margins)
  text( "<b>#{chapter.basename('.txt').sub(/\A\d+\s+/, '')}</b>",
  header_font_size )
  text( chapter.read.gsub("\n", "<br/>"),
  font_size.merge(:align => :justify) )
  end
  end
 end
 puts "</ol>"
 FileUtils.ln_sf(FILE, PDFS_DIR + "#{TITLE}.pdf")
 
 # Report success and finish up
 html_safe_path = ERB::Util.h(FILE.realpath).gsub("'", "&#39;")
 puts %Q{<p>Done: <a href="javascript: } +
  %Q{TextMate.system('open \\'#{html_safe_path}\\'', null);">} +
  %Q{open PDF</a>.</p>}
 html_footer
 TextMate.rescan_project

I’m sure that looks like a lot of code, but you may be surprised by how boring it really is when you give it a good look. I start by loading several libraries to save me work: four standard libraries that ship with Ruby, two sets of TextMate helpers, and then Prawn.

After that, I set a slew of constants and variables. I read the project path out of an environment variable that TextMate sets up and use that to build the paths I will need, as well as to figure out the project’s title. Then I ask the operating system for the name of the user. Finally, I read some options out of a YAML file, if it exists, and prepare a bunch of settings from that.

The next section is the real meat of the code. Using Prawn’s nice DSL, we construct a good-looking PDF. I’m using the Prawn Format extension in here, which allows for some basic HTML-like tags for bold and line-breaks. Note that I’m also writing status messages to $stdout as I work. I’m using HTML there too, and since I called the helpers html_header() and html_footer(), my output will be styled like a normal TextMate command.

The last chunk of code adds two important finishing touches. First, it builds a link to the generated PDF. I could have shown that file in the TextMate output window with a normal file path, but I decided it would be nicer to open the document in the system’s preferred PDF reader. To do that, I make use of the JavaScript function TextMate makes available to me for executing some shell code: TextMate.system(). The other important task is to use one last helper to get TextMate to rescan the project. This will make sure it notices the files I’ve added to the project.

Now, I made it possible to configure things like margins and fonts for the PDF with a YAML file. It doesn’t exist yet and the code will run fine without it, but let’s add one more shortcut for that. This time, we don’t need any code and we can just build a snippet. A snippet is a chunk of boilerplate code we can customize to our needs.

Click on that plus in the lower left of the Bundle Editor one more time and choose New Snippet this time. Give it a name like Configure PDF Builder and replace the examples with this code:

 ---
 # Set margins below. These are in PDF points (72 per inch).
 top_margin: ${1:72}
 left_margin: ${2:72}
 right_margin: ${3:72}
 bottom_margin: ${4:72}
 # Set font, size, and line spacing below.
 font: ${5:Courier}
 font_size: ${6:10}
 spacing: ${7:15}

This is just a trivial YAML document with some placeholders in it. The placeholders are the elements like ${1:72} where 1 is the placeholder’s order and 72 is the default value. When we trigger this snippet, TextMate will allow us to tab between those values in numerical order, replacing them with whatever we type. Try it out. Close up the Bundle Editor, create a file called pdf.yaml in config directory, and then go into the Bundles menu and select Fiction followed by Configure PDF Builder. Tap the tab key a few times and watch how your cursor jumps around. You can also use shift-tab to go back to previous values.

When things are configured the way you want them, it’s time to check out the rest of our hard work. Take another trip into the Bundles menu and choose Fiction then Build Fictional PDF. You should see the command output window pop up and run through your chapters. Then you can click the link to view the output. You should see a title page and sections for each chapter.

Further Automations

This is just one tool that would help us write fiction—among other things—with TextMate. There are a lot more we could build, more targeted to fiction. Personally, I would like to have a way to insert notes in the text that I can view as needed, but that wouldn’t really be a part of the story and wouldn’t print. I would also like a way to autocomplete character and place names no matter which file I’m in (overcoming a limitation of TextMate’s built-in autocompletion). It would also be neat to find a way to storyboard the chapters and allow for easy rearranging. I’m sure you could dream up more automations that would be handy as well.

iStock_000006242459Medium.jpg

That’s what makes TextMate or any other editor we can customize great, though. We can add on the exact tools that help our specific situation the most. Give it a shot. You might just be surprised how big the payoff can be.

I’ve placed the bundle created in this article on GitHub in case anyone wants to extend it some more. I should also mention for fiction authors that there is a Screenwriting bundle for TextMate.

James Edward Gray II is a Ruby and Rails programmer by day. However, he often spends hobby hours helping to maintain TextMate's Ruby integration. This gives him an ongoing excuse to play with his two favorite toys pretty much all the time. He is the author of TextMate: Power Editing for the Mac.