Using Emacs as a C++ IDE
28 Mar 2017Recently, I've had to code some C++ at work. And I saw it as a good opportunity to step up my Emacs' IDE game. I've eschewed clang-based tools until now, but GCC isn't adding AST support any time soon, and CEDET is too slow and too clumsy with macros for the particular project that I had. Here's the line in Eigen that broke the camel's back. Basically it's 30 lines of macros that expand to 30 lines of typedefs. Maybe it's a valid implementation choice, I'd rather avoid the macros altogether, but in any case I couldn't get CEDET to parse that.
Use Rtags for navigation
The first thing I tried
was rtags. My project was
CMake-based, so I just put this line in my subdirectory Makefile
:
cmake:
cd ../build && cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=1 ..
The -DCMAKE_EXPORT_COMPILE_COMMANDS=1
causes a
compile_commands.json
file to be emitted during the actual
compilation. This file describes the compile flags for every source
file. These flags are essential in helping the parser understand
what's going on.
Then, in the build
directory I start:
rdm & rc -J .
Finally, rtags-find-symbol-at-point
should work now. I still like to
use CEDET as backup, it's pretty good at tracking variables
defined in the current function:
(defun ciao-goto-symbol ()
(interactive)
(deactivate-mark)
(ring-insert find-tag-marker-ring (point-marker))
(or (and (require 'rtags nil t)
(rtags-find-symbol-at-point))
(and (require 'semantic/ia)
(condition-case nil
(semantic-ia-fast-jump (point))
(error nil)))))
(define-key c++-mode-map (kbd "M-.") 'ciao-goto-symbol)
(define-key c++-mode-map (kbd "M-,") 'pop-tag-mark)
For my other C++ projects which aren't CMake-based, I use the
excellent bear tool to emit the
compile_commands.json
file. It's as easy as:
make clean
bear make
Use Irony for completion
It didn't take long to figure out that rtags
isn't great at
completion. I almost accepted that's just the way it is. But this
morning I decided to make some changes and
try irony-mode. And it
worked beautifully for completion! What's ironic, is that irony-mode
doesn't have goto-symbol
, so the time spent to figure out rtags
was worth it.
Here's my Irony setup; I only changed the C-M-i binding to
the newly written counsel-irony
, now available in the counsel
package on MELPA:
(add-hook 'c++-mode-hook 'irony-mode)
(add-hook 'c-mode-hook 'irony-mode)
(defun my-irony-mode-hook ()
(define-key irony-mode-map
[remap completion-at-point] 'counsel-irony)
(define-key irony-mode-map
[remap complete-symbol] 'counsel-irony))
(add-hook 'irony-mode-hook 'my-irony-mode-hook)
(add-hook 'irony-mode-hook 'irony-cdb-autosetup-compile-options)
And here are some screenshots of counsel-irony
:
First of all, the completion is displayed inline, similarly to modern IDEs. You can use all of Ivy's regex tricks to complete your candidate:
Note how the power of regex matching allows me to narrow the initial
1622 candidates to only 22 functions that have src1
and src2
as
arguments. One of the candidates is cut off for being longer than the
window width. You can still match against the invisible text, but you
won't see it. It's possible to use C-c C-o (ivy-occur
) to
store the current candidates into a buffer:
Clicking the mouse on any of the lines in the new buffer will insert the appropriate symbol into the C++ buffer.
Outro
I'd like to thank the authors of rtags
and irony-mode
for these
nice packages. Hopefully, counsel-irony
is a nice addition. Happy
hacking!