rtags: the smart source code indexer
Table of Contents
1 Introduction
A source code indexing program lets developers quickly jump to definitions and references of variables, types and functions. The most advanced ones can even provide source code completion. It's a really useful tool to understand an existing code-base. Unfortunately C and especially C++ have complex context-free grammars and many tricky edge-cases that makes them hard to parse with LALR1 parsers generators (e.g. bison, yacc). Most C and C++ compiler front-ends are written by hand2 for this reason.
Most of the existing (open source) indexers written before Clang (e.g. ctags, exuberant ctags, etags) used their own parsers, which worked in most cases but would fail on certain things: conditional compilation, inheritance, virtual functions and template meta-programming comes to mind. Their database were also very simple: very basic type information, no knowledge of the compilation configuration (defines, include path, …). Now that Clang parser can be used as a library, a new breed of robust source code indexers are being released.
2 What is rtags
?
rtags
is a Clang based indexer made of 2 programs:
rdm
, a server that index files and handles database queries.rc
, the client to controlrdm
(make queries, set project configuration, …)
It has some nice features:
rdm
uses Linuxinotify
system to automatically re-index source file on changes. Since it knows about your project setup it will also follow dependencies when a file change occurs.- Indexing jobs are done in parallel. Files are indexed by worker threads and will use all your cores and processors by default.
rtags
is really aimed towards Emacs users and comes bundled with an Emacs minor-mode (vim users can look for a similar plug-in called YouCompleteMe3).rtags
tries to prioritize the indexing jobs so that it will re-index the files you are editing first.- It also does context-aware smart auto-completion although it seems broken at the moment.
Keep in mind that rtags
is still experimental software and is under
active development. Any help welcome!
3 Installation
4 Configuration
4.1 Project configuration
Once you have installed rtags
, run rdm
. You now have to teach
rdm
how your project is compiled. The basic way to do it is to prefix all your
compilation commands with rc -c
(e.g. rc -c gcc -I.. -Dfoobar -c blah.c
).
Depending on your build system it can be very easy. As soon as rdm
knows about your project it will start indexing.
The index database is saved and re-used when rdm
is started again.
4.1.1 CMake
With cmake
, you can do:
cmake . -DCMAKE_EXPORT_COMPILE_COMMANDS rc -J .
It will output a compile_commands.json
file rc
knows how to parse.
4.1.2 Ninja
With ninja
, you can use:
ninja -t commands | rc -c -
4.1.3 Makefile and other build systems
For anything else, you can use the small shell wrapper around gcc bundled with rtags and fully compile your project.
# my .zshrc # make sure ~/bin is in front of you $PATH function start-rtags-wrapper() { ln -s ~/prog/rtags/bin/gcc-rtags-wrapper.sh ~/bin/gcc ln -s ~/prog/rtags/bin/gcc-rtags-wrapper.sh ~/bin/c++ ln -s ~/prog/rtags/bin/gcc-rtags-wrapper.sh ~/bin/cc ln -s ~/prog/rtags/bin/gcc-rtags-wrapper.sh ~/bin/g++ } function stop-rtags-wrapper() { rm -f ~/bin/{gcc,cc,g++,c++} }
rtags
uses some heuristics like the name file you are editing or the
presence of a .git
directory to determine when you are using or
configuring different projects. The Emacs package should handle it
fine on its own but you can explicitely switch between projects with:
rc -w projectname
4.2 Emacs configuration
Add the rtags
elisp directory to your load-path and set the
keybindings.
;; if you built rtags manually you might need to do this (change path ;; accordingly) (add-to-list 'exec-path (expand-file-name "~/prog/rtags/bin")) ;; path to rdm/rc (add-to-list 'load-path (expand-file-name "~/prog/rtags/src")) ;; path to rtags.el (eval-after-load 'cc-mode '(progn (require 'rtags) (mapc (lambda (x) (define-key c-mode-base-map (kbd (concat "C-c r " (car x))) (cdr x))) '(("." . rtags-find-symbol-at-point) ("," . rtags-find-references-at-point) ("v" . rtags-find-virtuals-at-point) ("V" . rtags-print-enum-value-at-point) ("/" . rtags-find-all-references-at-point) ("Y" . rtags-cycle-overlays-on-screen) (">" . rtags-find-symbol) ("<" . rtags-find-references) ("-" . rtags-location-stack-back) ("+" . rtags-location-stack-forward) ("D" . rtags-diagnostics) ("G" . rtags-guess-function-at-point) ("p" . rtags-set-current-project) ("P" . rtags-print-dependencies) ("e" . rtags-reparse-file) ("E" . rtags-preprocess-file) ("R" . rtags-rename-symbol) ("M" . rtags-symbol-info) ("S" . rtags-display-summary) ("O" . rtags-goto-offset) (";" . rtags-find-file) ("F" . rtags-fixit) ("X" . rtags-fix-fixit-at-point) ("B" . rtags-show-rtags-buffer) ("I" . rtags-imenu) ("T" . rtags-taglist)))))
5 Usage
You should now be able to use rtags
on an indexed project. You can
try out some of the functions listed in the previous section. Here
are some examples:
5.1 Types and prototypes
- Open a file of the project in Emacs.
- Place the cursor on a variable and hit
C-c r M
- You should see the database information about the symbol (type, definition place, …).
- Doing the same thing on a function or method call will show the function prototype.
5.2 Symbol definition
- Place the cursor on a symbol.
- Hit
C-c r .
to jump to its definition. - Each jump is pushed in a stack and you can use
C-c r -
andC-c r +
to navigate in this stack.
5.3 Finding references
- Place the cursor on a symbol.
- Hit
C-c r /
to list all the places where the symbol is used. - The commands opens a new window where you can hit
RET
on each occurrence to jump to it.
6 Final words
rtags
is a really nice project that needs more contributors. If you
feel like helping, the code is hosted on Github4.
Footnotes:
Look-Ahead, Left-to-right, Rightmost derivation, https://en.wikipedia.org/wiki/LALR_parser
Both GCC and Clang use a handwritten recursive descent parser as of 2015.