Skip to content
/ rtags Public
forked from Andersbakken/rtags

A c/c++ client/server indexer for c/c++/objc[++] with integration for Emacs based on clang.

License

Notifications You must be signed in to change notification settings

hhfeng/rtags

 
 

Repository files navigation

MELPA MELPA
rtagsac-rtagscompany-rtagsflycheck-rtagshelm-rtagsivy-rtags
MELPAMELPAMELPAMELPAMELPAMELPA

Introduction

RTags is a client/server application that indexes C/C++ code and keeps a persistent file-based database of references, declarations, definitions, symbolnames etc. There’s also limited support for ObjC/ObjC++. It allows you to find symbols by name (including nested 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 updates 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 decent 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.

TLDR Quickstart

Build RTags

git clone --recursive https://github.com/Andersbakken/rtags.git
cd rtags
cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=1 .
make

Start the RTags daemon (rdm)

./bin/rdm &

Index the RTags project, and wait until rdm is silent

./bin/rc -J .

Open source file in emacs

emacs +73:34 src/rdm.cpp

Load rtags.el

M-: (load-file "rtags.el") RET

Insure rc can be found

M-x set-variable RET rtags-path RET "../bin" RET

Call rtags-find-symbol-at-point

M-x rtags-find-symbol-at-point RET

Your location is now on the definition of Server::instance()

Installing RTags

Prerequisites

There are a few prerequisites you need to have in order to build RTags

  • LLVM/Clang >= 3.3

    On Linux you may be able to use your distribution package manager to install this. On OS X you can use Homebrew or possibly other package managers, though see below for details if you plan to tag projects that use C++11 features (such as RTags itself). Alternatively you can grab the sources from llvm.org and build it yourself.

  • GCC/Clang >= 4.9/3.2

    A modern compiler to build RTags. RTags makes extensive use of C++11 features such as variadic templates and threading utility classes from the C++ standard library.

  • cmake >= 2.8

    The meta-built system used by RTags.


  • emacs >= 24.3 is recommended

    RTags might very well work with much older emacs versions but the oldest version we have tested with is 24.3. There’s no particular effort made to support older versions, but patches that make it work are welcome. Having said that, completion (ac, company) as well as Flycheck integration only works, and has been tested with emacs >= 24.3.

    NOTE: Emacs is no real prerequisite. That implies you can compile RTags without emacs installed. This may be of interest to you, if you want use RTags as back-end for a different editor like vim, see section Support for other editors. You explicitly can tell cmake to ignore the elisp files by passing -DRTAGS_NO_ELISP_FILES=1 to cmake.

Optional

  • pkg-config and bash-completion

    RTags comes with bash completion support. If you want the bash completion files to be installed, you either have to make sure that pkg-config and bash-completion package is installed or you can manually force the installation by setting the variable FORCE_BASH_COMPLETION_INSTALLATION to TRUE or ON.

    The default installation path is CMAKE_INSTALL_PREFIX/share/bash-completion/completions. You can adjust the path with the cmake variable BASH_COMPLETION_COMPLETIONSDIR variable. However, if the bash completions files are not installed in the default completion path, i.e. /usr/share/bash-completion/completions on GNU/Linux, you have to make sure the RTags bash completion file gets sourced to get completion support.

  • lua version >= 5.3

    TODO(Andersbakken): Add some notes why we optionally require Lua, whats the purpose of it.

    If your system does not provide a suitable version you can download Lua from here. We recommend downloading the pre-built binaries. If you have compiled Lua from scratch (the build will only create an archive liblua.a and no shared library) or you have installed the headers/library to a non standard place you need to tell cmake where the headers/library is located.

    E.g.

    cmake -DLUA_INCLUDE_DIR=/opt/lua/include -DLUA_LIBRARY=/opt/lua/lib/liblua.a
    # or liblua53.so or liblua53.a if you have downloaded, e.g. lua-5.3.2 binaries
    cmake -DLUA_INCLUDE_DIR=/opt/lua/include -DLUA_LIBRARY=/opt/lua/lib/liblua53.so
        
  • ZLIB
  • OpenSSL

C++11 on OS X

If you plan to tag projects using C++11 features on OS X then you’ll need a libclang linked with LLVM’s libc++. For LLVM 3.9 the following works:

brew install llvm --with-libcxx --with-clang --without-assertions --with-rtti

Note that unless you link llvm

brew link llvm

You’ll likely have to tell cmake where to find llvm-config.

E.g.

$ PATH=/usr/local/Cellar/llvm/3.9.1/bin/:$PATH cmake /path/to/rtags
$ make

or you can install clang and llvm from mac ports

sudo port install clang-3.5

Building RTags

  • Get the code

    To build RTags, you need to checkout RTags and the repository’s sub-modules, this can be done in one step. After that, you need to run cmake, make and make install, in that order.

    git clone --recursive https://github.com/Andersbakken/rtags.git
        

    You can also download the sources from here:

    https://andersbakken.github.io/rtags-releases/rtags-2.18.tar.bz2

    or

    https://andersbakken.github.io/rtags-releases/rtags-2.18.tar.gz

  • Building RTags

    We recommend building in a separate directory to keep the build files separate from the source, but you can run cmake in the source tree if you prefer.

    cd rtags
    mkdir build
    cd build
    cmake ..
    make
    make install
        

    TIP: You can run ccmake (CMake with an ncurses UI) instead of cmake to interactively configure the RTags build.

    Note that if you’re trying to test RTags by indexing RTags’ own source code you shouldn’t build it in tmp since RTags will refuse to index files in tmp

    “make install” runs ctest by default. If you want to skip ctest during install, you can pass -DSKIP_CTEST=True to cmake.

    cmake .. -DSKIP_CTEST=True
    make
    make install
        
  • Elpa

    You can install the elisp part of RTags using package.el if you add melpa to your sources list.

    Once you have emacs installed from melpa you can use the elisp to build the c++ parts of RTags. You still need to have a working compiler, libclang and cmake installed.

    To use:

    M-x rtags-install <ret>
        

    You may have to set additional arguments to cmake for this work. E.g.

    M-: (rtags-install nil "-DLIBCLANG_LLVM_CONFIG_EXECUTABLE=/usr/local//Cellar/llvm/4.0.0/bin/llvm-config") <ret>
        

Finding clang

Use Your System’s version

Unless you define RTAGS_BUILD_CLANG in cmake (or configure), the build system will try to locate the required llvm/clang libraries and options automatically from what is installed on your system.

RTags needs three pieces of information about libclang. All of these can be provided to cmake by way of an environment variable or a cmake variable. If not provided we will try to find llvm-config and interrogate it for the information. You can tell RTags which llvm-config to use like this:

LIBCLANG_LLVM_CONFIG_EXECUTABLE=/path/to/llvm-config cmake .

or

cmake -DLIBCLANG_LLVM_CONFIG_EXECUTABLE=/path/to/llvm-config .

If you don’t, we will look for variations of the llvm-config executable name in your $PATH. If llvm is installed at a different place, you could set the cmake variable CMAKE_PREFIX_PATH to the install prefix path of llvm.

The three things we need are:

  1. LIBCLANG_CXXFLAGS

    Usually something like this:

    $ llvm-config --cxxflags
    # Max OS X
    -I/usr/local/Cellar/llvm36/3.6.0/lib/llvm-3.6/include  -DNDEBUG -D_GNU_SOURCE -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -O3  -std=c++11 -fvisibility-inlines-hidden -fno-exceptions -fno-common -Woverloaded-virtual -Wcast-qual
    # Fedora 23 64 bit
    -I/usr/include -DNDEBUG -D_GNU_SOURCE -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -O3  -std=c++11 -fvisibility-inlines-hidden -fno-exceptions -fno-common -Woverloaded-virtual -Wcast-qual
        
  2. LIBCLANG_LIBDIR

    Usually something like this:

    $ llvm-config --libdir
    # Mac OS X
    /usr/local/Cellar/llvm36/3.6.0/lib/llvm-3.6/lib
    # Fedora 23 64 bit
    /usr/lib64/llvm
        

    We need this to locate clang’s system headers, and we will assume that they are located in: ${LIBCLANG_LIBDIR}/clang/CLANG_VERSION_STRING/include (/usr/local/Cellar/llvm36/3.6.0/lib/llvm-3.6/lib/clang/3.6.0/include). There should be headers like stdarg.h and limits.h in this directory.

  3. LIBCLANG_LIBRARIES

    Usually something like this:

    # Mac OS X
    /usr/local/Cellar/llvm36/3.6.0/lib/llvm-3.6/lib/libclang.so
    # Fedora 23 64 bit
    /usr/lib64/llvm/libclang.so
        

    Unless otherwise specified, we will try to find the clang library using cmake’s find_library feature and/or assuming that they there will be a libclang.(so|dylib) in ${LIBCLANG_LIBDIR}

Like with LIBCLANG_LLVM_CONFIG_EXECUTABLE these variables can be overwritten as a cmake variable (cmake -DLIBCLANG_LIBDIR =...) or an environment variable (LIBCLANG_LIBDIR =... cmake)

Let the Build System Download and Compile LLVM/Clang

By default, cmake searches for a system libclang. If you want it to download and build the required llvm/clang libraries, either call cmake with -DRTAGS_BUILD_CLANG=1 or configure with –build-clang

Note that if you use ninja to build RTags you might run into the following error:

ninja: error: '/usr/local/rtags-llvmclang/lib/libclang.dylib', needed by 'bin/rdm', missing and no known rule to make it

In that case you need to do this:

ninja llvmclang && ninja && ninja install

Setup

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. Since clang is a fully compliant compiler it needs specific information about how your sources are compiled to be able to properly index them. This is done through telling rdm about the compile line like this:

rc -c gcc -I... -fsomeflag -c foobar.c
rc -J /path/to/a/directory/containing/compile_commands.json

You can generate a compile_commands.json with various different tools, one might fit better than the other, depending on your project build system.

  • ninja
    ninja -t compdb cxx cc > compile_commands.json
    rc -J
        

    With ninja it’s also possible to pipe the commands directly to rc.

    ninja -t commands | rc -c -
    # Parse commands for a specific target only
    ninja -t commands rdm | rc -c -
        
  • cmake

    cmake can generate a compile_commands.json file as well.

    cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=1 .
    rc -J
        
  • Bear

    For other projects you can use bear to generate a compile_commands.json file. However, if you are cross-compiling you probably need to adjust the command entries in the compile_commands.json file to match the correct compiler. Furthermore, make sure you clean your project before invoking bear.

    make clean
    bear make
    rc -J
    # Parse commands for a specific target only
    make clean
    bear make rdm
    rc -J
        
  • make

    You can ask make to only print the recipes without actually doing something. This way may be perfectly fine for not too complex Makefiles.

    make clean
    make -nk | rc -c -
        

    There are very likely similar things you can do with other build systems that we’re unfamiliar with, please let us know if you do.

  • A different approach to get your files indexed is the man-in-the-middle

    This can be done 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'
        

    Now every time you compile a file with which gcc rc will get its grubby hands all over your command line and make sure RTags knows about it.

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 directory we try to find the source root based on similar heuristics around config.status/CMakeCache.txt etc. Usually this works out reasonably well. If it doesn’t for you, you can pass --project-root /path/to/the/project/root to rc.

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.

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

Integration with launchd (Mac OS X)

On Mac OS X, you can set rdm can be run on demand, on your behalf, by launchd, and have it exit cleanly after a period of inactivity. The easiest way to do this is with Homebrew:

brew services start rtags

You can also do it manually using the following steps:

  1. Create a file, e.g., in emacs, with the following contents:
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
    <plist version="1.0">
      <dict>
        <key>Label</key>
        <string>com.andersbakken.rtags.agent</string>
        <key>ProgramArguments</key>
        <array>
          <string>sh</string>
          <string>-c</string>
          <string>$RDM -v --launchd --inactivity-timeout 300 --log-file ~/Library/Logs/rtags.launchd.log</string>
        </array>
        <key>Sockets</key>
        <dict>
          <key>Listener</key>
          <dict>
        <key>SockPathName</key>
        <string>$HOME/.rdm</string>
          </dict>
        </dict>
      </dict>
    </plist>
        
  2. Replace $HOME with the absolute path to your home folder. Replace $RDM with the path to your copy of rdm, and add any command line parameters you might usually use.

    (The SockPathName entry relates to the name of the domain socket that rdm uses. The settings above are for the default value; if your command line options direct it to use some other name, please modify it to suit. Unfortunately launchd’s configuration files are a bit naff, so you’ll have to repeat yourself.)

  3. Save the result as ~/Library/LaunchAgents/com.andersbakken.rtags.agent.plist.
  4. Run the following command from the terminal:
    launchctl load ~/Library/LaunchAgents/com.andersbakken.rtags.agent.plist
        

    (This will happen automatically next time you log back in.)

  5. Try using RTags, and you should find rdm will spring into life!

Notes

  • rdm will automatically quit after 5 minutes of inactivity (this is what the --inactivity-timeout 300 command line option is for), so it won’t stick around hogging memory. But launchd will still be watching its socket for activity, and will relaunch it if necessary.
  • You can watch launchd’s logging by tailing ~/Library/Logs/rtags.launchd.log.

Integration with systemd (GNU Linux)

On GNU/Linux distributions based on the systemd service manager, rdm can also be socket acivated.

  1. Add the following to ~/.config/systemd/user/rdm.socket
    [Unit]
    Description=RTags daemon socket
    
    [Socket]
    ListenStream=%t/rdm.socket
    
    [Install]
    WantedBy=default.target
        
  2. Add the following to ~/.config/systemd/user/rdm.service
    [Unit]
    Description=RTags daemon
    
    Requires=rdm.socket
    
    [Service]
    Type=simple
    ExecStart=$RDM -v --inactivity-timeout 300 --log-flush
    ExecStartPost=/bin/sh -c "echo +19 > /proc/$MAINPID/autogroup"
    Nice=19
    CPUSchedulingPolicy=idle
        
  3. Replace $RDM with the path to your copy of rdm, and add any command line parameters you might usually use.

    You have to use absolute paths here. %h is expanded to your home directory. Environment variables are not expanded inside strings.

  4. Run the following command from the terminal:
    systemctl --user enable rdm.socket
    systemctl --user start rdm.socket
        

    Systemd will create the rdm socket automatically.

    Note that user level systemd does not work on RHEL derivatives. See here for more info: https://bugs.centos.org/view.php?id=8767

If you prefer using SystemV something like this could be used:

#!/bin/bash
#
# chkconfig: 35 90 12
# description: Foo server
#

# Get function from functions library
. /etc/init.d/functions

# Start the service FOO
start() {
        initlog -c "echo -n Starting FOO server: "
        /path/to/FOO &
        ### Create the lock file ###
        touch /var/lock/subsys/FOO
        success $"FOO server startup"
        echo
}

# Restart the service FOO
stop() {
        initlog -c "echo -n Stopping FOO server: "
        killproc FOO
        ### Now, delete the lock file ###
        rm -f /var/lock/subsys/FOO
        echo
}

### main logic ###
case "$1" in
  start)
        start
        ;;
  stop)
        stop
        ;;
  status)
        status FOO
        ;;
  restart|reload|condrestart)
        stop
        start
        ;;
  *)
        echo $"Usage: $0 {start|stop|restart|reload|status}"
        exit 1
esac

exit 0

Usage

Now that your files are indexed you can start using RTags. Normally you would do this from your editor but the way to extract this information from rdm is to use the command line tool rc.

E.g.

$ rdm &
$ ninja -t commands | rc -c
$ rc --follow-location Job.cpp:20:10
/home/abakken/dev/rtags/src/Job.h:10:18      List<RegExp> *mPathFiltersRegExp;

A location has the format of file:line:column.

For Emacs we maintain a set of elisp bindings that allows you to control RTags from your editor. There are projects that provide integration for other editors out there.

Vim: https://github.com/lyuts/vim-rtags and https://github.com/shaneharper/vim-rtags

Neovim: https://github.com/marxin/neo-rtags

Sublime Text: https://github.com/rampage644/sublime-rtags

Atom: https://github.com/artagnon/atomic-rtags, https://github.com/rajendrant/atom-rtags and https://github.com/sphaerophoria/atom-rtags-plus

rc has a vast number of commands and options and we have written a man page for it. Most users will have limited interest in ever calling them manually and would rather just use the interactive elisp functions.

Elisp

Functions

(rtags-start-process-unless-running)

Start the rdm process unless the process is already running. You may create hook to automatically call this function upon entering, e.g. c-mode or c++mode.

E.g.

(add-hook 'c-mode-hook 'rtags-start-process-unless-running)
(add-hook 'c++-mode-hook 'rtags-start-process-unless-running)
(add-hook 'objc-mode-hook 'rtags-start-process-unless-running)
(rtags-restart-process)

Restart the rdm process.

(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-c r. If you pass a mode to the function it will set it up on that mode, otherwise it will use c-mode-base-map). You can choose a different prefix than C-c r like this:

(rtags-enable-standard-keybindings c-mode-base-map "\C-xr")
(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)

Provides 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-symbol-info)

Print some info about symbol under cursor

(rtags-symbol-type)

Print the type of the 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.

(rtags-include-file)

Insert selected or entered include, e.g. “string.h”/<string.h> in current buffer, either at the top, after the first include statement or with prefix argument (C-u) at current point.

(rtags-get-include-file-for-symbol)

Insert include for entered symbol or symbol under courser in current buffer, either at the top, after the first include statement or with prefix argument (C-u) at current point.

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

Code Completion in Emacs:

To enable code completion in Emacs with company mode do the following:

  • Enable rtags-diagnostics. The easiest way is to:
(setq rtags-autostart-diagnostics t)

but you can also explicitly start it with

M-x rtags-diagnostics <RET>
  • Enable completions in RTags:
(setq rtags-completions-enabled t)
  • Enable company-mode
(require 'company)
(global-company-mode)
  • Add company-rtags to company-backends:
(push 'company-rtags company-backends)

This minimal init.el configuration should be enough to get completion to work.

(require 'package)
(package-initialize)
(require 'rtags)
(require 'company)

(setq rtags-autostart-diagnostics t)
(rtags-diagnostics)
(setq rtags-completions-enabled t)
(push 'company-rtags company-backends)
(global-company-mode)
(define-key c-mode-base-map (kbd "<C-tab>") (function company-complete))

To enable completion in Emacs with auto-complete-mode do the following: …TODO…

RTags Flycheck integration

To turn on RTags Flycheck support you need to load the flycheck-rtags package.

(require 'flycheck-rtags)

Optional

You may explicitly select the RTags Flycheck checker for some major modes for better experience.

At the moment there is no customize option available to choose between rtags-diagnostics overlays or Flycheck overlays, nor is it planned right now. We recommend setting flycheck-highlighting-mode locally to nil as the RTags overlays are more accurate.

Further, Flycheck will trigger automatically, based on events, the syntax checker for the current buffer, this is however, pretty useless in conjunction with RTags. We trigger it manually because we find it gives you a better experience. To turn off the automatic Flycheck syntax checking, set the variable flycheck-check-syntax-automatically locally to nil.

(defun my-flycheck-rtags-setup ()
  (flycheck-select-checker 'rtags)
  (setq-local flycheck-highlighting-mode nil) ;; RTags creates more accurate overlays.
  (setq-local flycheck-check-syntax-automatically nil))
(add-hook 'c-mode-hook #'my-flycheck-rtags-setup)
(add-hook 'c++-mode-hook #'my-flycheck-rtags-setup)
(add-hook 'objc-mode-hook #'my-flycheck-rtags-setup)

Helm integration

You can use Helm to view the result, to do so, set rtags-display-result-backend to helm. You don’t need to load helm-rtags, we do it. If you have installed RTags through MELPA you need to install helm-rtags.

(setq rtags-display-result-backend 'helm)

Ivy integration

You can use Ivy to view the result, to do so, set rtags-display-result-backend to ivy. You don’t need to load ivy-rtags, we do it. If you have installed RTags through MELPA you need to install ivy-rtags.

(setq rtags-display-result-backend 'ivy)

Videos

Here are some videos demonstrating how to use RTags with Emacs though some of these may be outdated:

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

Debugging RTags

If you find that rp is crashing (leading to output like this: “job crashed 191 9698036154370 0x331e7e30”). You should be able to do the following:

rdm --suspend-rp-on-crash

When rp crashes the rp process will stay alive, enabling you to debug it with something like this:

gdb -p `pidof rp`

Support for other editors

There are several other projects integrating RTags with other editors.

Sublime Text: https://github.com/rampage644/sublime-rtags

Vim: https://github.com/lyuts/vim-rtags https://github.com/shaneharper/vim-rtags https://github.com/mattn/vim-rtags

Neovim: https://github.com/marxin/neo-rtags

Note to those maintainers. If you need RTags to behave differently or add features to make these other integration’s easier (like produce output in other formats etc), just drop us a note.

Coding style

We are always excited to accept bug fixes, improvements and features through code contributions. Feel free to send a pull request for anything you think could be improved upon.

We’d like contributions to adhere to our coding style.

The coding style can kinda be deduced from this:

if (foo) {
    while (!done) {
        switch (value) {
        case 1:
            break;
        case 2: {
            int variable = 12;
            break; }
        case 3:
            break;
        }
    }
    for (auto foo : vars) {

    }
}

void test(const std::string &bar);

class Foo
{
private:
    int mPrivateMember;
};

struct Bar
{
    int publicMember;
};

class A
{
public:
    A(int val);
private:
    int mValue;
};

A::A(int val)
    : mValue(val)
{
}

conn->setErrorHandler([](const SocketClient::SharedPtr &, Message::MessageError &&error) {
        if (error.type == Message::Message_VersionError) {
            ::error("Wrong version marker. You're probably using mismatched versions of rc and rdm");
        } else {
            logDirect(LogLevel::Error, error.text);
        }
    });

Unit tests

There are some unit tests available both in rtags/automated_tests and in rtags/src/rct/tests. These can be run by cmake’ing RTags like this:

cmake -DBUILD_TESTS=1

and run like this:

make test

Disclaimer

RTags is still under development and is not the most stable piece of software you’ll ever find. We’re constantly working to improve on it.

About

A c/c++ client/server indexer for c/c++/objc[++] with integration for Emacs based on clang.

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • C++ 63.3%
  • Emacs Lisp 18.9%
  • CMake 13.4%
  • Shell 1.9%
  • Perl 1.8%
  • Python 0.2%
  • Other 0.5%