POSTS

Vacation, Week I, So Far: Textmate for Latin Students

Blog

So far this first week of vacation has been used, as Lauren astutely noted, as, effectively, study hall. This weekend was largely spent preparing for my Latin probatio on Tuesday. The good news is that I managed to catch up on all the optional exercises in the back of my Wheelock’s and I had an interesting opportunity to learn more about extending Textmate.

I have discussed elsewhere the power curve of the editor emacs versus vim, but think that Textmate may have hit the sweet spot in terms of writing extensions.

Extension 1: Writing Macrons

While I had earlier posted about my joy of finding a keyboard map which would allow me to enter macron-ized vowels with Option + Vowel, I found this to me a very straining key combination on my keyboard. It’s not that easy to hook a thumb under to hit option plus a vowel.

Thanks to my crazy remapped keyboard, where Control is where Caps Lock is, I was able to remap Control + Vowel -> Macronized Vowel. This is a much better typing arrangement ( try it: Push caps lock plus a/e/i/o/u ). Even nicer is that on a Dvorak keyboard, all of these combinations can be hit with the left hand solely.

While this may be nifty, you say, I can say that it’s even niftier because I was able to scope the effectiveness of these bindings to be within certain types of documents. While emacs and vim certainly both support this feature it was incredibly easy to implement in Textmate. I simply scoped this command set to be within the text.html scope and it was carried into text.html.markdown ( an HTML shorthand ) text.html.textile and good old text.html itself. This is huge, huge, huge. This means that when editing PHP or Perl code Control + “e” or macronized-e reverts to its default setting of “end of line”. Very handy.

{ Aside: Astute readers may note that by assigning Ctrl+e to be macron I lost a handy mnemonic key-binding for go-to-end-of-line. I did, in fact. I have remedied this by adding, in that same scope Control + “]” as a replacement and that’s worked out pretty well so far. }

##Extension 2: Speaking of Markdown

So key bindings and scoping may not be that big of a deal to you, but it was a huge step forward to me. Further, modal text editing is not something particularly foreign to vim or emacs. However, I had a few needs come up that would have been hairy to add in Vim’s scripting language, but that were incredibly easy to add with Textmate.

###Ascii Table

Often when answering the questions in the back of Wheelock the question takes the form of:

  1. Identify the personal endings of the future and imperfect tenses of the first two conjugations. 1) -o 2)-s 3)….

Obviously, constructing the answer in a table would be preferable with column headings being the given number, the given ending, the identification / the translation / etc. But the code for entering an HTML table is, while simple, a bit of a pain to enter. It’s very tag-heavy. Textmate has not done the best job of making this process easier.

In the php-markdown-plus wordpress extension, suppotr was added for Ascii tables. Why this hasn’t been added for the Markdown master code base I have no idea.

####Step 1: Create columns

There were six rows of data. Add plus one for the headers.

ruby -e ‘7.times{puts “|”}’

I type into textmate and push Ctrl+R. I get this:

 |
 |
 |
 |
 |
 |
 |

Textmate just let me type a Ruby one-liner into the text, hit Control R and insert the text. Not too shabby.

####Step 2: Fill in the table headers:

|person|imperfect|future|
|
|
|
|
|
|

####Step 3: Let’s fill in the given persons…

|person|imperfect|future|
|1st sg
|2nd sg
|3rd sg
|1st pl
|2nd pl
|3rd pl

Now it would be a pain to closs off all the row definitions with “|” by hand. I could use a search and replace expression like “s#$#|#g” on the highlighted range, but I wrote a simple key-binding to do that search and replace for me. I saved this add on into a bundle I created ( HTML Editing for Latin Students ) and voilá. Thus i hi-lit the section, control + | and…

####Step 4: Close off newly-added column

|person|imperfect|future|
|1st sg|
|2nd sg|
|3rd sg|
|1st pl|
|2nd pl|
|3rd pl|

Now I needed to fill in the subsequent columns…I use control +n to go to the next line so that I don’t have to use the mouse or the arrow keys. It makes the vertical editing task much easier. This is an emacs convention. I close the process off with Control + | again.

####Step 5: Finish off the table

|person|imperfect|future|
|1st sg|bam|
|2nd sg|bas|
|3rd sg|bat|
|1st pl|bamus|
|2nd pl|batis|
|3rd pl|bant|

and the future…

|person|imperfect|future|
|1st sg|bam|b?|
|2nd sg|bas|bis|
|3rd sg|bat|bit|
|1st pl|bamus|bimus|
|2nd pl|batis|bitis|
|3rd pl|bant|bunt|

Now, to my eye, this is an intelligible table. Nevertheless, I will need to convert this to an HTML table as Markdown doesn’t natively support this conversion. Now, if I were in emacs here, I’d need to break out some eLisp code. In vim I’d have to go through the hell of using the scripting metalanguage. With Textmate, I just write a ruby script and then paste that in ( it’s pasted after the jump ) and then bound that to a key-binding. Thus I have “Turn Ascii table into HTML” bound to Command-Option-T

<table border="0" cellspacing="5" cellpadding="5" style="">
  <tr>
    <th>person</th>
    <th>imperfect</th>
    <th>future</th>
  </tr>
  <tr>
    <td>1st sg</td>
    <td>bam</td>
    <td>b?</td>
  </tr>
  <tr>
  ...
</table>

And that was incredibly handy.

###Make styling with CSS easy

One of the things that made me choose HTML for my language of typing was the ease of styling. Per standard didactic purposes, there will be translation sections in exercises. I wanted to style the “given” one way and the answers another. Again, Textmate to the rescue.

Add a tab-trigger.

Typing: “pgiv(tab)”

<p class="sent_given">. </p>

I type a number (say, 11) then hit tab again and I’m inside the tag, typing the given.

Typing “pans(tab)”

<p class="sent_answer"></p>

Similar, but for answers. As both of these <p>-tag elements have classes associated with them, I can format them with CSS to get the look I want. Simple.

But wait, there’s more.

Doing ‘pans’ and ‘pgiv’ is inefficient. How about I type all the sentences at once, in paragraph form, start the paragraph with the question number to start at, and then have each of those set up as <p> tags with the “given” class associated with them?

11.  This is sentence one.  This is sentence two.  This is sentences three.

Highlight run through Ruby script I added called “Line-ify as Given sentences..”

<p class="sent_given">11.  This is sentence one.</p>
<p class="sent_given">12.  This is sentence two.</p>
<p class="sent_given">13.  This is sentence three.</p>

I’ve added other features like a corrections binding (corr-tab) to produce a span elemnent that’s colored red so that I can annotate errors, but I’ll stop here. You get the idea.

Textmate: Pretty, easy to extend with scripting languages you already know to key bindings you want in such a way that you don’t obliterate key bindings you don’t want to lose.

###Conclusion

I’ve been so impressed by the power of Textmate in the regard of making HTML so much easier to generate, that I’m thinking about extending these features to LaTeX to get the beautiful formatting power latent there. I hopes this has helped you see that Textmate isn’t just a wonderful editor for code-hackers, it’s great for classicists as well!

Ruby code

This is not the world’s cleanest Ruby. My Ruby had gotten a bit rusty. There are definitely some spots for improvement, but under the deadline of several Latin homeworks, I didn’t tweak this too heavily.

#!/usr/bin/env ruby

@table = Array.new
@table_attr = String.new
@css_attr= String.new
@row_width=0

def validate_table(t)
  @row_width = t[0].count("|")-1
  pipe_count = @row_width + 1
  t.each { |l|  exit!(1) if l.count("|") != pipe_count}
end

def clean_meta_lines(r)
  r.each  do
      |a|
      a.chomp!
      a.gsub!(/\|.*\>/,"")
      a.gsub!(/\|/,"")
      a.gsub!(/^\s+/,"")
    end

end

def create_table_hdrs(line)
  str = "  <tr>\n"
  line.gsub(/(^\||\|$)/,"").split(/\|/).each do |x|
    x.chomp!
    str += '    <th>' + x + "</th>\n"
  end
  str += "  </tr>"
end

def formulate_table_line(l)
  str = "  <tr>\n"
  l.gsub(/(^\||\|$)/,"").split(/\|/).each do |x|
    x.chomp!
    x.gsub!(/(^\s+|\s+$)/,"")
    str += '    <td>' + x + "</td>\n"
  end
  str += "  </tr>\n"
 str
end

while ( line = gets )
  @table.push line if (line =~ /^|.*|$/ && line !~ /^\|(CSS|ATTR)\>/)
  @table_attr = line.chomp if line =~ /ATTR\>/
  @css_attr = line.chomp if line =~ /CSS\>/
end

puts @table_attr
@css_attr = "style=\"" + @css_attr + "\"" if @css_attr
@table_attr= "border=\"0\" cellspacing=\"5\" cellpadding=\"5\"" unless @table_attr.length =~ /\w{3}/


@table_attr.chomp!
@css_attr.chomp!

validate_table(@table)
clean_meta_lines([@table_attr, @css_attr])
@table_hdrs = create_table_hdrs(@table.shift)
@table.shift if @table[0] =~ /\|\-{3}/

@table_def_line = ""

@table.each do
  |l|
  @table_def_line += formulate_table_line(l)
end

@table_def_line.sub!(/\n$/,"")

table_def = <<end_OF_STRING
<table #{@table_attr} #{@css_attr}>
#{@table_hdrs}
#{@table_def_line}
</table>
END_OF_STRING