7. Programming with TextMate
7.1 Beautiful Ruby in TextMate
If you’ve been writing Ruby code using TextMate, then you might have wished for an automatic code formatting capability. I didn’t find it built into TextMate, but I did find that it was very easy to add.
TextMate is Allan Odgaard’s popular and powerful text editor for Mac OS X. It has great support for projects involving multiple files and has some very powerful hooks allowing customization. Using language or application-specific “bundles”, TextMate users can add macros, shortcuts, and even run external programs to parse, modify, and replace all or part of a file being edited.
Although TextMate currently has no built-in Ruby code beautifier, Paul Lutus has written a Ruby code beautifier in Ruby. To add it to TextMate, open TextMate’s Bundle Editor window using the Window → Show Bundle Editor menu command. You’ll then see a window like the one below.

In the pane on the left-hand side, scroll down to the Ruby item and expand it. From the + menu at the bottom left, select the New Command menu item. You can name your command anything you like; I called mine “Beautify”. Click on the text field to the right of Activation: and press the key combination that you want to use to activate your command (I’m using Command-B). Then set the Scope Selector: field to “source.ruby” to ensure that your new command will only be run when you are editing Ruby source code. Finally, paste my slightly-modified version of Paul’s beautifier script into the command window (it’s below). When you’re finished, the Bundle Editor window should look like this:

Now any time you are editing a Ruby file, you can beautify it by pressing Command-B (or your chosen activation key). If something goes wrong or you don’t like the results, just use TextMate’s undo command (Command-Z) to go back to the original version.
The beautification code is below. I’ve made some trivial changes to Paul’s version to make it read from STDIN and write to STDOUT; since his version was released under the GPL, I believe that the GPL license terms still apply, but I waive all rights to my changes. With thanks to Paul and Allan, Happy Rubying!
#!/usr/bin/env ruby # Ruby beautifier, version 1.3, 04/03/2006 # Copyright (c) 2006, P. Lutus # TextMate modifications by T. Burks # Released under the GPL $tabSize = 2 $tabStr = " " # indent regexp tests $indentExp = [ /^module\b/, /(=\s*|^)if\b/, /(=\s*|^)until\b/, /(=\s*|^)for\b/, /(=\s*|^)unless\b/, /(=\s*|^)while\b/, /(=\s*|^)begin\b/, /(=\s*|^)case\b/, /\bthen\b/, /^class\b/, /^rescue\b/, /^def\b/, /\bdo\b/, /^else\b/, /^elsif\b/, /^ensure\b/, /\bwhen\b/, /\{[^\}]*$/, /\[[^\]]*$/ ] # outdent regexp tests $outdentExp = [ /^rescue\b/, /^ensure\b/, /^elsif\b/, /^end\b/, /^else\b/, /\bwhen\b/, /^[^\{]*\}/, /^[^\[]*\]/ ] def makeTab(tab) return (tab < 0)?"":$tabStr * $tabSize * tab end def addLine(line,tab) line.strip! line = makeTab(tab)+line if line.length > 0 return line + "\n" end def beautifyRuby commentBlock = false multiLineArray = Array.new multiLineStr = "" tab = 0 source = STDIN.read dest = "" source.split("\n").each do |line| # combine continuing lines if(!(line =~ /^\s*#/) && line =~ /[^\\]\\\s*$/) multiLineArray.push line multiLineStr += line.sub(/^(.*)\\\s*$/,"\\1") next end # add final line if(multiLineStr.length > 0) multiLineArray.push line multiLineStr += line.sub(/^(.*)\\\s*$/,"\\1") end tline = ((multiLineStr.length > 0)?multiLineStr:line).strip if(tline =~ /^=begin/) commentBlock = true end if(commentBlock) # add the line unchanged dest += line + "\n" else commentLine = (tline =~ /^#/) if(!commentLine) # throw out sequences that will # only sow confusion tline.gsub!(/\/.*?\//,"") tline.gsub!(/%r\{.*?\}/,"") tline.gsub!(/%r(.).*?\1/,"") tline.gsub!(/\\\"/,"'") tline.gsub!(/".*?"/,"\"\"") tline.gsub!(/'.*?'/,"''") tline.gsub!(/#\{.*?\}/,"") $outdentExp.each do |re| if(tline =~ re) tab -= 1 break end end end if (multiLineArray.length > 0) multiLineArray.each do |ml| dest += addLine(ml,tab) end multiLineArray.clear multiLineStr = "" else dest += addLine(line,tab) end if(!commentLine) $indentExp.each do |re| if(tline =~ re && !(tline =~ /\s+end\s*$/)) tab += 1 break end end end end if(tline =~ /^=end/) commentBlock = false end end STDOUT.write(dest) # uncomment this to complain about mismatched blocks #if(tab != 0) # STDERR.puts "Indentation error: #{tab}" #end end beautifyRuby
7.2 Beautiful Objective-C in TextMate
Not long after I started beautifying my Ruby in TextMate, I realized that I also needed a way to keep my C and Objective-C code in shape. Here’s how I do it using MacPorts and bcpp.
bcpp is a C and C++ source beautifier by Steven De Toni and maintained by Thomas E. Dickey. It also works fine on Objective-C code. You can get the source from the link above and build it yourself, or easily install it with MacPorts. To learn more about MacPorts, see James Duncan Davidson’s introduction. If you’ve installed MacPorts, you can just type “sudo port install bcpp” to install bcpp on your system. If you’re using the default MacPorts configuration, it will go into /opt/local/bin.
To use bcpp from TextMate, open the TextMate bundle editor (Bundles → Bundle Editor → Show Bundle Editor) and use the ”+” menu in the bottom left of the window to add a new command. I called the command “beautify” and configured it as shown in the window below.

The configuration refers to a bcpp config file that I’ve put in my home directory. There’s a listing of my .bcpp file below. You can also download it from this link.
; This file contains configuration parameters that are used ; within the bcpp program. ; There are two types of parameter types : Boolean, and Integer. ; Boolean types can have only two valid values [On, Yes, or Off, No]. ; Integer types can have a valid range of 0 - 5000. ;------------------------------------------------------------------------ ; This parameter specifies how many lines separate between two ; functions. ;------------------------------------------------------------------------ function_spacing = 1 ; Integer ;------------------------------------------------------------------------ ; Specifies whether to use tabs in indenting code. ;------------------------------------------------------------------------ use_tabs = no ; Boolean ;------------------------------------------------------------------------ ; Specifies how many spaces to indent. This parameter is also used ; for tab indenting, as 1 tab may be worth 8 spaces if so desired. ; This parameter is used to position comments in TAB mode, and expanding ; of tabs within code! ;------------------------------------------------------------------------ indent_spacing = 4 ; Integer ;------------------------------------------------------------------------ ; Specifies whether to indent preprocessor controls to match the code ;------------------------------------------------------------------------ indent_preprocessor = yes ; Boolean ;------------------------------------------------------------------------ ; Specifies whether to indent embedded SQL statements ;------------------------------------------------------------------------ indent_exec_sql = yes ; Boolean ;------------------------------------------------------------------------ ; Defines at what start position comments that have code on the ; same line to be placed. ;------------------------------------------------------------------------ comments_with_code = 50 ; Integer ;------------------------------------------------------------------------ ; Defines at what start position comments with no code start. ;------------------------------------------------------------------------ comments_with_nocode = 0 ; Integer ;------------------------------------------------------------------------ ; Set this option to ON turns off setting indentation position of parameter ; "comments_with_nocode". Indentation is then set according to code ; position. ;------------------------------------------------------------------------ leave_comments_nocode = yes ; Boolean ;------------------------------------------------------------------------ ; Use this option is used to change non-ascii (non-printable) chars to ; octal notation if they lie within quotes. Either ; Ascii_Chars_Only, XOR Leave_Graphic_Chars parameters need to be set ; as a True value for this parameter to take effect. ;------------------------------------------------------------------------ NonAscii_Quotes_to_Octal = yes ; Boolean ;------------------------------------------------------------------------ ; Setting this parameter to yes will strip non-printable characters ; from the source files, but leave any character that are IBM ; graphics alone. Any non-printable characters that lie within ; quotes will be transformed into octal/character notation, if ; NonAscii_Quotes_To_Octal parameter is set to True. ;------------------------------------------------------------------------ ; leave_graphic_chars = yes ; Boolean ;------------------------------------------------------------------------ ; Setting this parameter to yes will strip any non-printable, ; non-ascii characters from the input file. Any non-printable ; octal/character notation if NonAscii_Quotes_To_Octal is set to ; True. Comment out this parameter if you are using ; Leave_Graphic_Chars parameter, as this parameter will override ; it. ;------------------------------------------------------------------------ ascii_chars_only = no ; Boolean ;------------------------------------------------------------------------ ; This parameter will place open braces on a new line after it's ; associated code if set on/yes. Else the brace will be place on ; next above line if possible, with it's code. ;------------------------------------------------------------------------ place_brace_on_new_line = no ; Boolean ;------------------------------------------------------------------------ ; This parameter will stop output from the program corrupting output ; that may exit from the program via the standard output. ; If this parameter is set to off/no then no output is generated from ; the program, unless an error is encountered ;------------------------------------------------------------------------ program_output = no ; Boolean ;------------------------------------------------------------------------ ; Specifies what the internal memory requirements will be in size of the ; line processing buffer. This essentially is used only for open brace ; relocation in kernighan/ritchie style. ;------------------------------------------------------------------------ Queue_Buffer = 10 ; Integer ;------------------------------------------------------------------------ ; If this option is set to true then the input file will be backup into a ; another file with a ".bac" extension added to the end of the file ; name. ;------------------------------------------------------------------------ Backup_File = yes ; Boolean
Did you find an error? Is something missing? Post your comment or suggestion below!
Comments (0) post