My emacs setup for C++

Aug 16, 2012

I tried pretty hard to get existing solutions like ecb and semantic to work, but they seem to have fallen into a state of disrepair. I’d tried both a while back so I don’t remember exactly how they weren’t working, but both were throwing errors that had something to do with the changes in Emacs 24. I also tried to get the clang plugin working, but clang itself is pretty finicky and hard (for a beginner like me) to set up in windows, so I gave up on that pretty quickly and went back to my trusted Ctags solution that worked well in my Vim days. The solution is a combination of a bunch of things, including:

  • etags for generating a TAGS file that’s used for symbol lookup and file navigation (comes bundled with Emacs 24)
  • imenu for listing functions in a file
  • Directory-local variables for compilation from inside Emacs
  • Mingw32 for compilation and debugging via gdb

I’ve attached most of the required files at the end of this post, but here are a few notes and gotchas in no particular order

  • For generating tags, etags doesn’t support recursively searching for files the way ctags does. To get around this, I use “find” to list all headers and source files in a directory and then pipe them to etags. I haven’t tried with the MSys version of find for windows, but the cygwin version prints paths with a /cygdrive before them, which is not how Emacs sees paths. Therefore, I used the find.exe that comes with the excellent msysgit package. I’ve attached the same to this post, it works on its own with the attached msys-1.0.dll. However, I recommend against putting this anywhere in the path, as it will overshadow the FIND command that comes with windows, which can result in unintended consequences. So, my create-tags function accepts a “find-file” argument to which you can pass the location of the find.exe executable supplied. I haven’t figured out how to make this optional (since linux and OS X will have find in their path already) so if you do, please let me know.
  • Since I’m using cmake for my current project and my build directory is different from the source directory, I modify the compile-command inside a .dir-locals.el in my project’s root. This came with its own complications since Emacs considers modifying compile-command on a per-directory basis to be “risky”, resulting in it whining every time I opened a new file in that directory. And since it’s a risky change, there is no option of saying “!” to force the idiot to remember, I was being forced to press “y” every time, which quickly became annoying. Hence this function:
(defun risky-local-variable-p (sym &optional _ignored)
  (if (string-match "compile-command" sym) (nil)
(progn
  (condition-case nil
      (setq sym (indirect-variable sym))
    (error nil))
  (or (get sym 'risky-local-variable)
      (string-match "-hooks?$\\|-functions?$\\|-forms?$\\|-program$\\|\
      -commands?$\\|-predicates?$\\|font-lock-keywords$\\|font-lock-keywords\
      -[0-9]+$\\|font-lock-syntactic-keywords$\\|-frame-alist$\\|-mode-alist$\\|\
      -map$\\|-map-alist$\\|-bindat-spec$" (symbol-name sym))))))
  • I’m basically overwriting a similarly-defined risky-local-variable-p that comes bundled with emacs. I don’t know if there’s an equivalent of “super::” in elisp, so that’s why I ended up copy-pasting from the original function into mine.
  • I can compile my code from any subdirectory of my project’s root with “M-x compile”. Jump back-and-forth between errors with “M-g n” and “M-g p”. Once I’ve fixed errors, I can recompile with “M-x recompile” which doesn’t ask you to confirm the makeprg (path to binary used for compilation, which in our case is the path to mingw32-make). For more info, check out the wiki page.
  • I’m also using Ido which comes bundled with Emacs 24 and is pretty useful for auto-completion in the minibuffer. My config also hooks into IDO by using ido-completing-read for file navigation and go-to-symbol.
  • Choose every available option while installing Mingw32 since I didn’t bother exploring the options to see which one installs gcc and which one installs gdb and which one shoots you in the foot. As far as I know, you need to add the Mingw32/bin directory to Path to make my method work. Don’t add the msys/bin to the path. Also ensure that “sh” or “bash” aren’t in your path, as this causes some random issue with mingw32-make when run from inside Emacs and causes “M-x compile” to fail with a highly-informative “mingw32-make.exe: Interrupt/Exception caught (code = 0xc0000005, addr = 0x41f97e)” where the “addr” value will vary.

Look for “global-set-key” in cpp.el for keybindings I’ve defined. A binding I haven’t defined is C-* which takes you back to where you were before you jumped to a symbol’s definition with 'etags-select-find-tag (M-.). And if my bindings seem weird to you, that’s because I use a Dvorak layout.

Files referenced in this post

Tagged: