Skip to content

xsyr/rtags

 
 

Repository files navigation

Introduction

RTags is a client/server application that indexes c/c++ code and keeps a persistent in-memory database of references, declarations, definitions, symbolnames etc. It allows you to find symbols by name (including class and namespace scope). Most importantly we give you proper follow-symbol and find-references support. We also have neat little things like rename-symbol, integration with clang’s “fixits” (http://clang.llvm.org/diagnostics.html). We also integrate with flymake using clang’s vastly superior errors and warnings. Since rtags constantly will reindex “dirty” files you get live updating of compiler errors and warnings. Since we already know how to compile your sources we have a way to quickly bring up the preprocessed output of the current source file in a buffer.

While existing taggers like gnu global, cscope, etags, ctags etc do a good job for C they often fall a little bit short for C++. With its incredible lexical complexity, parsing C++ is an incredibly hard task and we make no bones about the fact that the only reason we are able to improve on the current tools is because of clang (http://clang.llvm.org/). RTags is named rtags in recognition of Roberto Raggi on whose C++ parser we intended to base this project but he assured us clang was the way to go. The name stuck though.

Usage

RTags is meant to be used like this:

rdm runs in the background and monitors all your indexed files for changes and reindexes when a source file or one of its dependencies is modified.

To get information about a symbol or find references to it you would use the command line client rc.

E.g.

$ rc --follow-location Job.cpp,400
/home/abakken/dev/rtags/src/Job.h,2186      List<RegExp> *mPathFiltersRegExp;

We maintain a set of elisp bindings so you don’t acually have to call this yourself but we feel that this architecture allows us to add bindings to other editors (like vim) at a later point.

rc has a vast number of commands and options and we intend to write a man page at some point. Most users will have limited interest in ever calling it manually and would rather just use the interactive elisp functions.

Architecture

Since clang requires us to really compile the file we need to know the actual c(xx)flags for the source files. In essence RTags indexes all the source files it has been told about and all the headers included by these source files. To tell rtags about a source (you only need to do this once, after that it will monitor the file for changes using inotify/kqueue) you need call this command:

$ rc --compile "gcc -W... -I... -D... source.c"

One could do this manually or script some per/project parsing of Makefiles/CMakeLists.txt/etc but the way we normally recommend doing it is like this:

$ ln -s /path/to/rtags/bin/gcc-rtags-wrapper.sh /somewhere/that/is/in/your/path/before/usr/bin/gcc
$ ln -s /path/to/rtags/bin/gcc-rtags-wrapper.sh /somewhere/that/is/in/your/path/before/usr/bin/c++
$ ln -s /path/to/rtags/bin/gcc-rtags-wrapper.sh /somewhere/that/is/in/your/path/before/usr/bin/cc
$ ln -s /path/to/rtags/bin/gcc-rtags-wrapper.sh
/somewhere/that/is/in/your/path/before/usr/bin/g++

E.g.

$ which -a gcc | xargs file
/home/abakken/bin/gcc: symbolic link to `/home/abakken/dev/rtags/bin/gcc-rtags-wrapper.sh'
/usr/bin/gcc:         symbolic link to `gcc-4.7'

This script will essentially stick itself in the middle of your compiles and notify rtags that you want this file indexed and then invoke the next entry in the $PATH that matches $0. This is a similar approach to the one taken by distributed build systems like icecream (https://github.com/icecc/icecream).

RTags will group source files into projects based on some heuristics.

Essentially it will look for certain files/dirs (like configure/CMakeLists.txt/scons.1/.git) etc to try to determine the likely project root for each source file. For generated source files that end up in the build dir we try to find the source root based on similar heuristics around config.status/CMakeCache.txt etc. Usually this works out reasonably well.

RTags’ only gives you information about current project when you ask for things by name. You can explicitly change the current project using:

$ rc -w foobar

We try to do it automatically for you by passing along information about the current buffer when we call rc from elisp so that rdm can update its current project on demand.

RTags keeps a cache of indexed data so you don’t have to reindex everything if you restart it. Note that reloading this data sometimes takes quite a few seconds (could be 10+ for big projects) and emacs will be blocked until it is finished if an emacs command triggered this reload. There’s not a lot of good options to avoid this. We definitely don’t want to make Emacs’ rc integration async since we’d then have to move your cursor behind your back when the command eventually finished and nobody wants that. We’re looking into better options.

The location of this data is by default ~/.rtags but can be overridden by passing –data-dir other/dir to rdm or putting something like this in your ~.rdmrc:

$ cat ~/.rdmrc
--data-dir=/other/dir

Installing rtags:

cd /path/to/rtags
cmake .
make 3rdparty # This step will take quite a while. It will download trunk of clang and llvm and build it
make
export PATH=/path/to/rtags/src:~/bin/:$PATH
ln -s /path/to/rtags/gcc-rtags-wrapper.sh ~/bin/c++
ln -s /path/to/rtags/gcc-rtags-wrapper.sh ~/bin/cc
ln -s /path/to/rtags/gcc-rtags-wrapper.sh ~/bin/g++
ln -s /path/to/rtags/gcc-rtags-wrapper.sh ~/bin/gcc
rdm & # This starts the rtags daemon

Add something like this to your .emacs:

(add-to-list 'load-path "/path/to/rtags"))
(require 'rtags)

To enable the standard keybindings (using a prefix of C-x r) you could call something like this:

(rtags-enable-standard-keybindings c-mode-base-map)

Elisp

There are lots of interactive functions to call:

(rtags-find-symbol-at-point)

Follow symbol under cursor. For references this goes to the definition (or declaration if no definition is known of the symbol. For declarations it goes to the definition and vice versa. For definitions of variables/parameters with constructors it goes to the constructor in question. If you pass a prefix argument, limit to current source file, if you pass a prefix argument and have narrowed the current file, limit to the narrowed region. This prefix argument is the same for: rtags-find-references-at-point, rtags-find-symbol, rtags-find-references

(rtags-find-references-at-point)

Find all references to symbol under cursor. If symbol is itself a reference it will find all references to the referenced symbol

(rtags-find-symbol)

Prompt for name of symbol to go to. Imagine the following code:

namespace N
{
class C
{
public:
    int func(int);
};
};

using namespace N;
int C::func(int val)
{
    return val * 2;
}

int N::C::func(int) will now be accessible by the following names:

  • func
  • func(int)
  • C::func(int)
  • C::func
  • N::C::func(int)
  • N::C::func
(rtags-find-references)

Prompt for name of symbol to find references to. Same as above but find references to symbol rather than declarations and definitions.

(rtags-diagnostics)

Start an async process in a buffer to receive warnings/errors from clang whenever a file gets reindexed. It integrates with flymake to put highlighting on code with warnings and errors

(rtags-enable-standard-keybindings)

Sets up a ton of standard keybindings under C-x r (we try to avoid crashing with the register shortcuts). If you pass a mode to the function it will set it up on that mode, otherwise it will use c-mode-base-map).

(rtags-find-file)

Lets you jump to file by name (partial or full, concept kinda stolen from gtags.el) with completion in the project. This includes all files under what we determine to be the root of the project, not just source files.

(rtags-find-virtuals-at-point)

For virtual functions, show the various reimplementations of the function at point

(rtags-fixit)

Apply clang’s automatic fixits in current file. If you pass a prefix arg use ediff to apply it. See (http://clang.llvm.org/diagnostics.html) for more info.

(rtags-imenu)

Provices an ido-based imenu like interface to a subset of the symbols in the current file. Note that it does not actually use imenu infrastructure.

(rtags-location-stack-back)
(rtags-location-stack-forward)

Whenever rtags jumps somewhere it pushes a location onto its stack. Jump back and forward in this stack

(rtags-next-match)
(rtags-previous-match)

For functions that return more than one match, jump to the next/previous one.

(rtags-preprocess-file)

Preprocess current file according to known C(XX)Flags and show the
result in a buffer. If region is active only display the
preprocessed output for that region.
(rtags-print-cursorinfo)

Print some info about symbol under cursor

(rtags-print-dependencies)

Open a buffer showing files that depend on current file/files that current file depends on.

(rtags-print-enum-value-at-point)

Print integral value of enum value at point

(rtags-quit-rdm)

Shut down rdm

(rtags-rename-symbol)

Rename symbol under cursor. Make sure all files are saved and fully indexed before using.

(rtags-reparse-file)

Explicitly trigger a reparse of current file. Mostly for debugging. Unless we have bugs it should not be necessary.

(rtags-show-rtags-buffer)

Switch to RTags buffer. This is the buffer where a number of functions display their alternatives when they have more than one match.

Variables:

rtags-path

Path to rc/rdm if they’re not in $PATH.

rtags-jump-to-first-match

Similar to compilation-auto-jump-to-first-error. Whether to jump to the first match automatically when there’s more than one.

rtags-find-file-case-insensitive

Whether to match files case-insensitively

rtags-find-file-prefer-exact-match

Whether to exclude partial matches for file names when an exact match is found. E.g. /foobar.cpp /bar.cpp If rtags-find-file-prefer-exact-match is t a query for bar.cpp would only return /bar.cpp, otherwise both foobar.cpp and bar.cpp would be returned.

  • Fall back to other taggers: You can do something like the following to fall back to e.g. gtags if rtags doesn’t have a certain project indexed:
(defun use-rtags (&optional useFileManager)
  (and (rtags-executable-find "rc")
       (cond ((not (gtags-get-rootpath)) t)
             ((and (not (eq major-mode 'c++-mode))
                   (not (eq major-mode 'c-mode))) (rtags-has-filemanager))
             (useFileManager (rtags-has-filemanager))
             (t (rtags-is-indexed)))))

(defun tags-find-symbol-at-point (&optional prefix)
  (interactive "P")
  (if (and (not (rtags-find-symbol-at-point prefix)) rtags-last-request-not-indexed)
      (gtags-find-tag)))
(defun tags-find-references-at-point (&optional prefix)
  (interactive "P")
  (if (and (not (rtags-find-references-at-point prefix)) rtags-last-request-not-indexed)
      (gtags-find-rtag)))
(defun tags-find-symbol ()
  (interactive)
  (call-interactively (if (use-rtags) 'rtags-find-symbol 'gtags-find-symbol)))
(defun tags-find-references ()
  (interactive)
  (call-interactively (if (use-rtags) 'rtags-find-references 'gtags-find-rtag)))
(defun tags-find-file ()
  (interactive)
  (call-interactively (if (use-rtags t) 'rtags-find-file 'gtags-find-file)))
(defun tags-imenu ()
  (interactive)
  (call-interactively (if (use-rtags t) 'rtags-imenu 'idomenu)))

(define-key c-mode-base-map (kbd "M-.") (function tags-find-symbol-at-point))
(define-key c-mode-base-map (kbd "M-,") (function tags-find-references-at-point))
(define-key c-mode-base-map (kbd "M-;") (function tags-find-file))
(define-key c-mode-base-map (kbd "C-.") (function tags-find-symbol))
(define-key c-mode-base-map (kbd "C-,") (function tags-find-references))
(define-key c-mode-base-map (kbd "C-<") (function rtags-find-virtuals-at-point))
(define-key c-mode-base-map (kbd "M-i") (function tags-imenu))

(define-key global-map (kbd "M-.") (function tags-find-symbol-at-point))
(define-key global-map (kbd "M-,") (function tags-find-references-at-point))
(define-key global-map (kbd "M-;") (function tags-find-file))
(define-key global-map (kbd "C-.") (function tags-find-symbol))
(define-key global-map (kbd "C-,") (function tags-find-references))
(define-key global-map (kbd "C-<") (function rtags-find-virtuals-at-point))
(define-key global-map (kbd "M-i") (function tags-imenu))

Videos

Here are some videos demonstrating how to use rtags with emacs:

Set up rtags

Set up symlinks and run the daemon

Project setup using make

Project setup using ninja

Navigation/references

Fixits

“IMenu” / virtuals / filenames

Rename symbol

Enums and cursor info

Disclaimer

RTags is still in development and is not the most stable piece of software you’ll ever find. We crash sometimes (though admittedly mostly inside clang). We’re constantly working to improve on it.

About

A c++(and possibly others)-aware tagger based on clang.

Resources

Stars

Watchers

Forks

Packages

No packages published