Using Emacs as a C++ IDE
By on July 17, 2016
An updated version of this post is available
here.
Last year I wrote a post
about using Flymake with Emacs to get on-the-fly
syntax checking. Recently I watched a cppcon lightning talk by
Atila Neves on his setup for using Emacs as a C++ IDE and was inspired
to adapt this to my own needs. In this post I will guide you through
the setup process, which will carry over to Aquamacs for the most part
too. I’ll try to note any differences as they come up. This guide will
also use Homebrew for installing some additional software,
but on Ubuntu you should be able to get it from apt. Please note that
I take no credit for this setup at all. It is all thanks to the many
developers and blog authors whose content I was able to use and
piece together. I will do my best to cite any resources as I go. I
apologize in advance for anything I missed.
Additional Software Installation
First we will install the necessary backends using brew and then
move on to the actual Emacs setup. All of the packages listed here should
be installed by running brew install package
. I recommend that at this
stage you run brew update
to make sure you are getting the latest version
of all the packages. Let us first install the package Bear (Build
EAR) for recording how your project is
compiled. It won’t be necessary for all projects, but definitely for some.
To install it, run brew install bear
. If you use LaTeX I recommend you
install the package chktex
using brew as well.
In order to be able to use RTags to navigate your project
you should install LLVM using
brew install llvm --with-libcxx --with-clang --without-assertions --with-rtti
.
I suggest you follow the RTags website for the most up-to-date
installation instructions. It may look long, but it is rather straight forward
to do. You can actually install RTags from brew by running brew install rtags
,
but note that you still need LLVM. Note that we will be changing the
way we launch rdm
later (if you don’t know what rdm
is, see here).
One really cool thing about RTags is that it is not specific to Emacs.
You can use it with vim, sublime, Atom, and possibly other editors too.
The last package I suggest you install is clang-format
, which allows you to
format parts or the entirety of your code to a a certain style. This is
extremely useful for ensuring uniform style across a project and for
maintaining readability. ClangFormat also is not tied to Emacs and can be used
stand alone or in other editors.
As a side note, if you want to try the clang-complete part of the IDE (I use
Flycheck’s clang-mode), you will probably have to install the
emacs-clang-complete-async
package from brew or from the project’s
GitHub page.
Emacs Configuration
Below I’ll go over my Emacs init file (~/.emacs
) in a sort of section by section
approach.
General Settings
What often annoys me in Emacs is that I have to type “yes” or “no”. I’d much prefer
to just type “y” and “n”. This can be done (thanks to the post here)
by adding the following to your init (~/.emacs
) file:
I also find the #...#
auto-save files annoying, so let’s disable that by adding
On OSX you will have to set additional paths in Emacs so that it can find the packages you install from brew. Add the following to the start of your init file:
Now, the easiest way to install all the packages we will use is to add the following to your init file:
This will automatically install any packages listed in the
package-list
variable if it is not already installed.
If you are using a recent version of Emacs (24.3 or newer)
then everything should install without any issues.
If you are using Aquamacs on OSX then you may need to comment
out the lines below ;; install the missing packages
and install
the packages yourself manually.
On The Fly Syntax Checking: Flycheck
Compiling code or running through an interpreter takes time. Time is probably
our most valuable personal resource. I’ve recently made the change from
Flymake to Flycheck. One of the reasons is that
it can be integrated with
a compile_commands.json
file that is generated by Bear. I also
have to write some python code on occasion, and there too I don’t want to
spend time running the code to check for errors. Flycheck offers an extension,
flycheck-pyflakes
that does python syntax checking really well. Combining
these, I get a short section for my init file, but you will want to
use cmake-ide
to get the most out of it. Here is the relevant code:
Now there are sometimes some annoying things with Flycheck. While working on a large (and quickly growing) project I found that I sometimes got bizarre errors such as not being able to find a certain type, specifically a class we wrote, or even that there were errors in one of the included STL headers. This seemed really strange to me, especially because RTags syntax checking did not find these errors (I use RTags and Flycheck for syntax checking of larger projects). To make things even stranger, the code compiles fine. So after much stumbling around I think I finally understand what the issue is. Flycheck seems to parse the code local to the buffer, and so if that code does not compile stand-alone then you get strange errors. This could be seen as a good thing since it will force developers to explicitly include headers they use rather than using functions or classes because they are included in a header file that’s being included. So the lesson here is that if you get strange errors, don’t look at the first error, look at all of them and see if it could be related to missing headers preventing Flycheck from analyzing the code locally.
cmake-ide and RTags
Flycheck integrates nicely with the cmake-ide
package. The nice thing
about cmake-ide
is that it also sets up RTags for your project. To enable
RTags and cmake-ide, add the following to your init file:
You might notice that I set a keyboard shortcut, C-c m
, for compiling
with cmake-ide.
Now let me discuss RTags and what I had to do to get cmake-ide to behave the
way I wanted it to. I’ll start with RTags. RTags needs the r-daemon (rdm) to
be running to handle generating and finding the tags. This is reasonable and
you can customize the behavior of the daemon a lot. Just look at the manual
entry and you’ll see that it really allows for a lot of customization. One
thing that troubled me was that by default when it re-indexes a project it
runs the process rp
on 8 threads and with a nice of -1
. Now my notebook only
has 4 physical cores, so with hyperthreading that means it’s running at full
capacity for a thread that really should be a background thing I hardly notice.
The reason this is annoying is because if you are working on a file that is
a dependency of a large portion of your project RTags re-indexes a large portion
of the project, or at least does a lot of work. Since cmake-ide starts the rdm
on its own, I had to change the way it behaves. The other problems I encountered
were that cmake-ide does not use multiple threads for building and also doesn’t
seem to have an option for Build EAR. Because my changes to cmake-ide are
currently hard-coded I have not contributed them back to the project, but let
me tell you the way you can recompile cmake-ide to behave better.
The first thing I changed was to have cmake-ide call make using Build EAR and
to build using 8 threads so that I don’t have to wait very long for the build
to finish. To do this you must find the cmake-ide.el
file. It could be
located in ~/.emacs.d/elpa/cmake-ide...
or if you’re using Aquamacs it will be
in ~/Library/Preferences/Aquamacs Emacs/Packages
.
I changed the line in the function
cmake-ide--get-compile-command
that says
((file-exists-p (expand-file-name "Makefile" dir)) (concat ...
to say
((file-exists-p (expand-file-name "Makefile" dir)) (concat "bear --append make -j8 -C " dir))
You can specify however many threads you want to build with and if you don’t
want to build with Bear then simply remove bear --append
. Next, to get
rdm to run on only 2 cores and have a larger nice value I changed the function
cmake-ide-maybe-start-rdm
by adding "-j 2" "-i 40" "-a 10"
after
cmake-ide-rdm-executable
in the start-process
call. Here the -a
specifies
the niceness and -i
the number of translation units to cache. Now you have
recompile cmake-ide by doing M-x
and then running byte-force-recompile
and
selecting the directory that cmake-ide.el
is in. Do not specify the file
itself, just the directory.
The last piece of advice for using RTags is that you shouldn’t save very often. Every time you save a file it gets re-indexed so if you change something small and start thinking about the next line don’t immediately save, unless you want RTags to be re-indexing continually and using up your computing resources.
RTags allows you to quickly navigate around your code. Here is an example
of jumping straight to the header file where the class is declared that
is actually intentionally performed a bit slower than you can do in practice:
Since I consider legible code part of having correct code I’ll mention
ClangFormat here. ClangFormat automatically formats your code
to conform to a specific style. There are several built-in presets but you
can also add your own. ClangFormat will search up directories until it reaches
a .clang-format
file and then uses that. This means you can specify
formatting for each project individually by placing a .clang-format
file
in the root directory of your project. Since it’s a text file you can even
track it with git. One nice things is that we can actually call ClangFormat
to format the selected line or region. For this to work add the following
to your init file:
I use the keyboard shortcut C-M-tab
but you may change this if you wish.
Helm
Helm is a framework for incremental completions that allows fuzzy matching and a variety of other powerful features. For example, integrating helm with git allows you to search your project for files no matter which directory you are currently in. Helm’s search is also case-insensitive meaning you don’t have to deal with pesky CamelCase spelling of filenames. Helm also offers similar features for the command-prefix (Meta-x), the buffer list, mini buffer list, finding files, ctest (which you are using, right?) and many others that I have not yet explored. You can also integrate it with Flycheck and Flyspell to get nicer windowing and navigation with them. I’ll discuss Flyspell in more detail below, but for now, here is my helm configuration:
To activate helm-flycheck use the keyboard shortcut C-c ! h
and
to use helm-ctest C-c t
. When in helm-ctest you can type the name
or partial name of a test to limit the tests disabled. To run a test
press enter (I use C-m
) and to select several tests to run use
press C-space
on each test you want to execute, then press enter.
The tests will run and you will get immediate feedback on whether
or not they passed.
Code Completion: Company, Irony and Semantic
Note: This part of the post is still somewhat in progress. I’m happy with the current behavior but may find bugs and improvements as time goes on. If there are any, I’ll post updates here.
First thing, company
stands for “Complete Anything” and so you can
probably guess we will leverage it as our backend for code completion.
It is also useful for all around completion. The other nice thing is
we can use company
to get STL and C library header completion,
query RTags for completions, ask clang to give completions for the
entire STL, and still use Yasnippet for custom completions to save
us typing out mundane things like for loops.
Let’s start with getting Yasnippet set up. Yasnippet comes with several useful snippets for code completion, but to really get the most out of it you will want to use the snippets from here
I’ve found that there are some missing that I will add and also that some seem to add incorrect code or don’t work.
Now let’s load up company and the backends we will want to use.
I’ve generally found that semantic completion is quite slow
and gives irrelevant results. However, semantic is really nice
for navigating around a file using helm. The shortcut I’ve set
is C-c h i
. For navigating between files I still recommend RTags.
I also provide functions my-enable-semantic
and my-disable-semantic
to enable and disable semantic completion so you can easily change
it if you wish. The code in my init file is:
To start company completion just press tab. I’ve found that sometimes
it will timeout and not offer any completions. When this happens
I find that most of the time pressing tab again gives the completions.
Sometimes it may appear to hang for a brief period of time. This I
believe occurs when RTags is being queried for completions. You can
navigate the completions menu using M-n
and M-p
then pressing
enter to select the desired completion. Note that return types
in C++ are denoted using the postfix syntax --> type
.
Directory local variables can be very useful for setting behaviors for
individual projects. For example, setting the build path, CMake flags,
or compiler flags is best done by using directory local
variables. These are set in a file called .dir-locals.el
and Emacs
searches up the directory tree until it finds one so you only need to
add it to the root directory of your project. Here is an example of
how to set the directory local variables for a project
Without setting directory local variables the code completion and
syntax checking tools may not know how to properly compile your code,
which means they also cannot function properly since they compile your
code to see if what you are changing is functional. What I have
noticed is that sometimes you need to restart Emacs (Aquamacs) in
order for the directory local variables to properly take effect. This
seems to be true if you open a source file of your project before
adding the .dir-locals.el
file.
Before moving on I want to show you some of the useful things that
you can auto-complete using this infrastructure.
If you know the first few letters of a function you can quickly complete
the entire member function name with the correct arguments:
If you cannot remember the member functions then you can get a full list
too. This is a bit finicky sometimes, but here is an example of what
the list looks like:
Finally, you can use the snippets from Yasnippet to get an entire class
template complete with move and copy constructors and assignment operators.
This is just an example. You can add any snippets
and shortcuts you want, which is what makes Yasnippet so great.
Spell Check: Flyspell
Something that I find to be quite useful is on-the-fly spell checking. Flyspell offers this for all your buffers and restricts itself to comments when checking your code. Nobody wants an embarrassing spelling mistake in their comment and this fixes that. The code I use to set up Flyspell is:
For a list of corrections I again use helm (helm-flyspell
).
To pull up the options move the cursor over the word you
want to correct and then type F8
(on a Mac you must use the
fn
key). Here is an example of what this setup looks like
in action:
Miscellaneous
In order to keep this post to a somewhat reasonable length I’ll end here with Magit and cmake-mode. My Magit configuration is
and to load cmake-mode I use
I have customized my Emacs configuration a great deal more. My current
init file is ~650 lines long, whatever that’s worth. I have a header
that shows the current function and the path to the current file,
but this required customizing the font a great deal to make it
legible and nice. I’ve uploaded a complete version of my Emacs file
here and will eventually make a public copy on
either GitHub or BitBucket. If you want you can simply copy the
entire contents of init.el
into your ~/.emacs
file and you should
automatically have the entire configuration set the way I do.
I hope you found this guide useful and please email me with any questions, feedback and/or suggestions.