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
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/rubyrequire “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)elseFile.open(tmp_file, ‘w’) { |fout| fout.print selected }enduser = 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)
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)
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”)
@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.
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!
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!
The problem is not emacs, but it is a perl problem.
You can use the ruby version proposed in the comment @TomMurray.
Thanks