Friday, 22 May 2009

Switching between Header and Implementation Files with Emacs

I often find myself working with C or C++ code that follows the pattern of parallel source/include directories, probably with lots of layers in between (e.g. project/include/some-controller/some-aspect/something.h and project/src/some-controller/some-aspect/something.cpp). Quickly switching between both saves on keystrokes and sanity, so I wrote this little chunk of elisp, bound to ctrl-alt-g (for no particular reason other than the combination is easy to mash):

(defun djw-c-toggle-impl-header-view (create-if-nonexistent)
(interactive "P")
(let* ((mode (if (string-equal (file-name-extension (buffer-file-name))
:switch-to-implementation :switch-to-header))
(toggle-tags (list (cons "/Include/" "/Src/")
(cons "/include/" "/src/")))
(from-fn (if (eq mode :switch-to-implementation)
#'car #'cdr))
(to-fn (if (eq mode :switch-to-implementation)
#'cdr #'car))
(source-file-name (buffer-file-name))
(case-fold-search nil)) ;; I want case-sensitive matching throughout this block.
(dolist (pair toggle-tags)
(setf source-file-name (replace-regexp-in-string (funcall from-fn pair)
(funcall to-fn pair)
(setf source-file-name
(if (eq mode :switch-to-implementation)
"h\$" "cpp\$")
(if (eq mode :switch-to-implementation)
"cpp" "h")
(message (format "Looking for %s" source-file-name))
(if (or (file-exists-p source-file-name)
(find-file source-file-name)
(message (format "Can't find '%s'" source-file-name)))))

If the counterpart file is missing, it won't be created unless the function is invoked with a prefix argument (C-u C-M-g).

There's probably some extension to CC mode or the like that already does something like this, but in the absence of reading the manual, this works well for me.

1 comment:

  1. Thank you very much!

    I looked for something packaged with emacs (24) that does what you provided, but I couldn't find anything (just ff-find-other-file, which needs explicit search directories to switch between header/implementation). I had to adapt your source a bit since I use hpp instead of h for my header files, but apart from that, it's working very well. :-)