Home > R, TextMate > TextMate Emacs-like indentation (for R files)

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.

UPDATE: The perl code does not work on Leopard (at least my Leopard with Emacs 22.1.1 + Textmate 1.5.9).

This version in Ruby works:

#!/usr/bin/ruby
require “ftools”
# Handle selection (less useful, probably) or full file.
tmp_file = “/tmp/textmate-tidy.#{$}.R”
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
user = ENV['USER'] ||”
user_opt = “-u #{user}” unless (user.empty?)
cmd = “emacs -u \”#{user}\” -batch \”#{tmp_file}\” -eval \”(require ‘ess)\” -eval \”(indent-region (point-min) (point-max) nil)\” -f ess-fix-miscellaneous -f save-buffer &> /dev/null”
system(cmd) || raise(“Failed to indent with Emacs.”)
system(“cat #{tmp_file}”)
File.delete(tmp_file)
Categories: R, TextMate
  1. Tom Murray
    September 27, 2008 at 6:28 pm | #1

    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. Dan Gunter
    December 3, 2008 at 1:25 am | #2

    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”)

  3. VaudtVarken
    February 8, 2009 at 10:43 am | #3

    @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.

  4. Sergiy Nesterko
    May 17, 2010 at 7:48 pm | #4

    Hi, I tried implementing this method on Leopard with TextMate 1.5.9 and built-in emacs 22.1 and it doesn’t work even though ESS was installed successfully (ran it within emacs). What happens is that the command sort of ‘freezes’ and I have to interrupt its execution. After this the selected text disappears, so probably the matter is in the emacs part. Could you advice how to fix the problem? Indeed, TextMate’s indentation sort of sucks and I was really hoping that this would work.

    Thanks from Cambridge MA!

  5. Sergiy Nesterko
    May 17, 2010 at 9:15 pm | #5

    Hi, thanks for help, but I figured it out. Rewrote the script in Bash and it works now. For the emacs batch mode, what worked for me was –eval “(require ‘ess-site)”, not –eval “(require ‘ess)” as stated in your example.

    Thanks for the creative idea though!

  6. gragusa
    May 17, 2010 at 9:17 pm | #6

    The problem is not emacs, but it is a perl problem.

    You can use the ruby version proposed in the comment @TomMurray.

    Thanks

  1. September 27, 2008 at 6:53 pm | #1
  2. December 2, 2008 at 12:19 am | #2

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.