Using rtags ang gtags for coding Ruby
Boring intro
When I was a C and Java developer (not that long ago), I always used
the GNU GLOBAL source code tag
system gtags.
Despite it’s awful, impossible-to-find-in-google name, it’s a wonderful system to navigate around your code: find function definitions, where they are used, fast “grepping” of code, you name it.
And, most important, it’s emacs support is wonderful!!
You can check their tutorial. It’s a little bit scary at the beginning, but, really, you should try it.
I want to show you how i use gtags when coding Ruby. Hopefully it
will be useful to somebody else.
First problem: gtags only supports C, C++, Java, PHP, YACC ¿¿??
“out of the box”.
Fortunately, it’s developers are very smart and have thought of a
system where you can use any program that outputs a pretty established
format out of the code you want to tag (anything that emits output like
ctags -x does).
So, you can use, for example, Exuberant
ctags, to parse your Ruby code and
then use gtags to navigate it.
Exuberant ctags it great for parsing lot’s of languages, but not so
good with Ruby. But don’t despair, there’s this guy who has wrote
rtags, a
parser designed to extract code tags from Ruby code. This is more
Ruby-especific, so it usually gets much more information (for example,
attr_accessor :foo , alias :foo :bar).
Also, with rtags, it’s possible to tag not only my_method, but
also MyClass#my_method, so you can list, for example, every method
in one class.
Problems: rtags is slow. Painfully slow, compared with ctags. But
gtags is pretty smart, I told you. The first time you index your
sources it’ll be slow. But, after changing and changing your code,
it’ll just reindex the files that have changed since it’s last
reindex.
Get the code
First, you should have installed gtags (apt-get install global)
Now, you need to get a rtags version that can output cros xref
format:
I have patched rtags, so you can use it with gtags (I am not that
smart, it was easy, since the rtags code is easily read).
I have placed it in github, in http://github.com/gaizka/rtags/tree/master.
If you don’t care about git, you can get the file at: http://github.com/gaizka/rtags/tree/master%2Fbin%2Frtags?raw=true
Once you have, place anywhere in your disk, chmod +x, and add that
direcotry to your $PATH.
Update The rtags mantainer has merged my modifications into the official repo, so can install directly with gems:
$ gem install rtags --version 0.97
Just make sure it’s versión 0.97 or higher.
Configure it
With the installation of gtags comes a sample configuration file that you have to use to extend, to use it with rtags. First copy the sample:
cp /usr/share/doc/global/examples/gtags.conf $HOME/.globalrc
And then add this lines. Careful with it, this format is awful, and you cannot have any blank lines on it.
(You can get the whole file here: http://pastie.org/290127
# We need to change some of the output that rtags dumps # # Module::Submodule is NOT found by global (don't know why) # Module:Submodule IS found # So we replace :: with : # # Also, dotnames are not allowed in tags # So, when rtag parses this # class User ; def self.validate ; end ; end # as: # User.validate line_no file_name line # global doesn't find it with: global -c User # So we need to replace any dots in the firts column with # # User#validate line_no file_name line # # So we need to to this: # rtags -x %s | awk '{ sub(/^::/, "") ; sub(/::/, ":"); sub(/\\./, "#", $1); print}' # rtags:\ :tc=common:\ :suffixes=s,a,sa,asm,C,H,cpp,cxx,hxx,hpp,cc,c,h,y,rb:\ :extractmethod:\ :GTAGS=rtags -x %s | awk '{ sub(/^\:\:/, "") ; sub(/\:\:/, "\:"); sub(/\\./, "#", $1); print}':\ :GRTAGS=gtags-parser --langmap=java\:.rb -dtr %s:\ :GSYMS=gtags-parser --langmap=java\:.rb -dts %s: #
Last, you need to tell gtags that you want to use the rtags
parser. To do so, yo need to set the environment variable:
export GTAGSLABEL=rtags
Test it
OK, we now should be able to roll!
Let’s index, for example, activerecord. Go to your gems directory, and try it (you should adapt your paths accordingly):
cd $HOME/gem_repository/gems/activerecord-2.0.2
export GTAGSLABEL=rtags
$ gtags -v
[Fri Oct 03 19:54:48 CEST 2008] Gtags started.
Using config file '/home/gaizka/.globalrc'.
[Fri Oct 03 19:54:48 CEST 2008] Creating 'GTAGS'.
[1] extracting tags of lib/active_record.rb
[2] extracting tags of lib/active_record/attribute_methods.rb
...
...
[174/174] extracting tags of test/serialization_test.rb
[Fri Oct 03 19:55:43 CEST 2008] Done.List methods starting with find_:
$ global -x "^find_*" find_all_ordered 337 test/associations/eager_test.rb def find_all_ordered find_by_sql 531 lib/active_record/base.rb def find_by_sql find_every 1230 lib/active_record/base.rb def find_every find_first 22 lib/active_record/associations/has_and_belongs_to_many_association.rb def find_first ...
ActiveRecord errors:
$ global -x "^ActiveRecord:Errors*" ActiveRecord:Errors 19 lib/active_record/validations.rb class Errors ActiveRecord:Errors#[] 119 lib/active_record/validations.rb alias :[] ActiveRecord:Errors#add 64 lib/active_record/validations.rb def add ActiveRecord:Errors#add_on_blank 79 lib/active_record/validations.rb def add_on_blank ActiveRecord:Errors#add_on_empty 70 lib/active_record/validations.rb def add_on_empty ...
Command line is great, but when you are editing, you are editing, so
you shouldn’t have to drop to the console when you are using your $EDITOR.
Vim support
I just use vim for quick edits, so i cannot help you here. Just point you to gtags documentation: http://www.gnu.org/software/global/globaldoc.html#SEC26
Emacs support
If you’re using the one true editor, you’re lucky. Gtags support is great.
If you want to use gtags in all your ruby coding, then add this lines
to your .emacs:
(autoload 'gtags-mode "gtags" "" t) (add-hook 'ruby-mode-hook (lambda () (gtags-mode 1) (setq gtags-symbol-regexp "[A-Za-z_:][A-Za-z0-9_#.:?]*") (define-key ruby-mode-map "\e." 'gtags-find-tag) (define-key ruby-mode-map "\e," 'gtags-find-with-grep) (define-key ruby-mode-map "\e:" 'gtags-find-symbol) (define-key ruby-mode-map "\e_" 'gtags-find-rtag)))
Now, open one of the files of activerecord.
cd $HOME/gem_repository/gems/activerecord-2.0.2 emacs lib/active_record.rb
We need to tell gtags where to look for it’s parsed files:
M-x gtags-visit-rootdir
And point it to $HOME/gem_repository/gems/activerecord-2.0.2
After this, let’s look for tags starting with Time
M-x gtags-find-tag <RETURN> rename <TAB><TAB>
You should get the completions:
rename_column rename_table
Choose, for example, rename_table and you should get a window with
contents like:
rename_table 110 abstract/schema_statements.rb def rename_table rename_table 419 mysql_adapter.rb def rename_table rename_table 584 postgresql_adapter.rb def rename_table rename_table 212 sqlite_adapter.rb def rename_table rename_table 373 sqlite_adapter.rb def rename_table
Choose the line you want to go to, and bingo!, you should be there.
You can search inside modules by “tabbing” into them.
M-x gtags-find-tag ActiveRecord<TAB><TAB>:Callbacks<TAB><TAB>#create_with_callbacks
One think i haven’t managed to is to get rtags emit it’s format so
you can find both ActiveRecord:Callbacks#create_with_callbacks and
Callbacks#create_with_callbacks. I’ll try to do it, so stay tunned!
You CAN find both ActiveRecord:Callbacks#create_with_callbacks and
just create_with_callbacks, though.
Finding not just functions/classes definitions
Finding function definitions is great. But sometimes you need to know where one method is called, for example.
You can then use one of the next functions:
gtags-find-rtag. This one finds function references. It does not that good with Ruby, usually it finds more lines than desired. Works great with C or Java, though.gtags-find-with-grep. This one uses grep for finding occurences in the lines of the code.gtags-find-symbol. This one finds where “symbols” (not Ruby symbols, more like “tokens”)are used.
For example, to look where NotImplementedError are raised, you can
do:
M-x gtags-find-symbol NotImplementedError
NotImplementedError 34 database_statements.rb raise NotImplementedError, "select_rows is an abstract method"
NotImplementedError 39 database_statements.rb raise NotImplementedError, "execute is an abstract method"
....Or, where ALTER TABLE is used:
M-x gtags-find-with-grep ALTER TABLE
ALTER%20TABLE 122 schema_statements.rb add_column_sql = "ALTER TABLE #{quote_table_name(table_name)} ADD #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
ALTER%20TABLE 131 schema_statements.rb execute "ALTER TABLE #{quote_table_name(table_name)} DROP #{quote_column_name(column_name)}"
....Reparsing files
Ok, you have coded loads of new functionality, and you want it to get
tagged. You don’t need to execute gtags again, it’s too slow. You
need to call this (in the directory where GTAGS file is).
$ global -u -v
And it will only parse modified files since last parse, so it is
pretty fast (don’t forget to export
GTAGSLABEL=rtags to tell it to use rtags).
The end
I hope this is useful to somebody. If you need any aclaration, don’t hesitate in leaving a comment, i’ll try to help you with it.
Thanks to all the great developers out there that release free-as-in-freedom software. ¡¡Thanks for making our coding sessions easier!!
Discussion Area - Leave a Comment