TextMate Emacs-like indentation (for R files)

If you use OS X, TextMate is hands down the best editor available for this platform. It integrate perfectly with the OS X environment. While the Project Window is probably the greatest thing about TextMate for the everyday user, its extensibility is what makes it a wonderful companion in editing.

The biggest complain I have about TextMate is that it does not handle indentation very well. I write a lot of R code and I am used to the indentation provided by ess (Emacs Speak Statistics). Frustrated, I decided to do something about it. After looking for option I realized that I could combine the feature of Emacs with those of TextMate to improve indentation. In practice I was able to use emacs (with ess) as beckend for the indentation. If you want to try follow the following steps:

1. Install ess (Emacs Speak Statistics) from http://ess.r-project.or in /usr/share/emacs/site-lisp;

2. In TextMate go to Bundles|Bundle Editor|Show Bundle Editor…

3. Create a new command, say Tidy

4. Add the following script:

#!/usr/bin/perl
my $in;
my $now = "tidyRcode";
my $file = "/tmp/tmptx_${now}.R";
my $eb=$ENV{'TM_BUNDLE_SUPPORT'};
open FILE, ">$file" or die "unable to open $file $!";
while () {
print FILE $_;
}
close(FILE);
`emacs -batch --eval "(require 'ess)" ${file} --eval '(indent-region (point-min) (point-max) nil)' -f ess-fix-miscellaneous -f save-buffer &> /dev/null`;
my $in = `cat ${file}`;
print $in;

5. Select the following options:

  • Input: Selected Text or Document;
  • Output: Replace Selected Text;
  • Key Equivalent: Shift+Cmd+H;
  • Scope Selector: source.r

You can see the picture below with my setting

TextMate Emacs-like indentation (for R files)

Now you are ready to go. While you are editing R code, simply select and press Shift+Cmd+H and you will see your code….the ess way.

5 Responses to “TextMate Emacs-like indentation (for R files)”

  1. Tom Murray Says:

    Great idea! Textmate’s indentation drives me crazy, and sometimes makes me want to go back to really-long-command-strings-with-counter-intuitive-names-in-emacs.

    I did a little variant in Ruby that can handle any language for which an Emacs mode exists. It uses an external script file (this should be put in the Textmate support dir, but I’m lazy) which takes the file extension as a parameter, so you can add “Tidy” commands to any Textmate language, like this:

    /path/to/tidy.rb cpp # C++
    /path/to/tidy.rb ml # OCaml ;-)
    /path/to/tidy.rb java # Java

    Here’s the code for tidy.rb itself:

    #!/usr/bin/ruby

    require ‘ftools’

    # Use ending to tell Emacs what language mode to use.
    ending = ARGV[0]
    raise(‘No ending specified’) if (ending.nil? || ending.empty?)

    # Handle selection (less useful, probably) or full file.
    tmp_file = “/tmp/textmate-tidy.#{$$}.#{ending}”
    selected = ENV['TM_SELECTED_TEXT'] || ”
    if (selected.empty?)
    raise(‘No selected text or file.’) if (ENV['TM_FILEPATH'].empty?)
    File.copy(ENV['TM_FILEPATH'], tmp_file)
    else
    File.open(tmp_file, ‘w’) { |fout| fout.print selected }
    end

    # Do the indentation, loading user Emacs file (to handle user indent customizations).
    user = ENV['USER'] || ”
    user_opt = “-u #{user}” unless (user.empty?)
    cmd = “emacs -batch #{user_opt} ‘#{tmp_file}’ -eval ‘(indent-region (point-min) (point-max) nil)’ -f save-buffer &> /dev/null”
    system(cmd) || raise(“Failed to indent with Emacs.”)
    system(“cat #{tmp_file}”)
    File.delete(tmp_file)

  2. i/o » Blog Archive » De-sucking Textmate indentation Says:

    [...] I ran across this nice post to indent R files using Emacs in batch mode from Textmate. A very simple and effective idea (Emacs [...]

  3. NetLogger Blog » Script to indent R code Says:

    [...] found this page describing a way to do it with ESS (Emacs Speaks Statistics). So, I downloaded ESS from the ESS [...]

  4. Dan Gunter Says:

    You can also use R itself to do this, as documented in the R Extensions manual under “Tidying R Code”

    options(keep.source = FALSE)
    source(”myfuns.R”)
    dump(ls(all = TRUE), file = “new.myfuns.R”)

  5. VaudtVarken Says:

    @Dan
    Your solution doesn’t indent the code.
    Try indenting a line that’s not in a function and that contains x<-rnorm(mean=0,sd=1,100) ; # generate a 100 normally distributed numbers
    Your solution is static.

Leave a Reply