Tuesday, March 2, 2010

Switch between c/c++ source and header files in Emacs

A pretty common task when editing c/c++ programs is to switch between the source and header files (i.e. file.c or file.cpp and file.h). I had a handy keyboard shortcut in vim to do this and, despite my newfound appreciation for the concept of Emacs, this was one of those things that in practice I was sorely missing. I actually sort of expected it to be built in... M-x switch-from-source-to-header or something, but I didn't find it (please let me know if it exists!). So I did what you're supposed to do with Emacs, I forged my own. It turned out to be a pretty good exercise in learning elisp. I had to figure out how to do if-then-else, a switch statement, some simple logic, how to deal with strings, how to interact with emacs, etc. For example, there's no "else" in elisp, instead you do something like this.
(if (some condition)
    (then action)
  (else action)
)
However, the actions are limited to a single statement. If you want to use more than one, you have to wrap it in a (progn ), like this.
(if (some condition)
    (then action)
  (progn
    (else action 1)
    (else action 2)
    )
)
Anyways, here's my function to switch between source and header files. I'm sure there's a better way to do it. There's a couple shortcomings here - it only knows about ".h", ".c", and ".cpp" extensions and if there is no ".c" or ".cpp" file corresponding to a header, it opens up a ".cpp" file for you (maybe you wanted ".c"...). There ought to be a way around the first problem - you could at least specify a configurable table of extension associations. The second problem may be more of a design decision problem. It works pretty well for me - I develop primarily in c++, so assuming ".cpp" is fine for me. If you want the opposite behavior, just swap ".cpp" and ".c" so they are checked in the opposite order. Of course you could ask for an input, but that goes beyond the scope of what I wanted. Another open question for me is how elisp treats local and global variables. I've purposely chosen short, unintelligible variables names here to avoid clobbering some other global variable. I think the "let" command has something to do with this, but I didn't have time to work it out.
(defun dts-switch-between-header-and-source ()
  "Switch between a c/c++ header (.h) and its corresponding source (.c/.cpp)."
  (interactive)
  ;; grab the base of the current buffer's file name
  (setq bse (file-name-sans-extension buffer-file-name))
  ;; and the extension, converted to lowercase so we can
  ;; compare it to "h", "c", "cpp", etc
  (setq ext (downcase (file-name-extension buffer-file-name)))
  ;; This is like a c/c++ switch statement, except that the
  ;; conditions can be any true/false evaluated statement
  (cond
   ;; first condition - the extension is "h"
   ((equal ext "h")
    ;; first, look for bse.c 
    (setq nfn (concat bse ".c"))
    (if (file-exists-p nfn)
        ;; if it exists, either switch to an already-open
        ;;  buffer containing that file, or open it in a new buffer
        (find-file nfn)
      ;; this is the "else" part - note that if it is more than
      ;;  one line, it needs to be wrapped in a (progn )
      (progn
        ;; look for a bse.cpp
        (setq nfn (concat bse ".cpp"))
        ;; likewise 
        (find-file nfn)
        )
      )
    )
   ;; second condition - the extension is "c" or "cpp"
   ((or (equal ext "cpp") (equal ext "c"))
    ;; look for a corresponding bse.h
    (setq nfn (concat bse ".h"))
    (find-file nfn)
    )
   )
  )
(global-set-key (kbd "C-c s") 'dts-switch-between-header-and-source)

6 comments:

Fernando Nascimento Nakamura said...

Hey man, are you one of the developers of the glswarm application? I was searching for some swarm simulators and found this, but no documentation, reference to the developers, nothing. I tracked your sourceforge's profile and found this blog. Could you mail me so we can talk more? If you want of course. Thanks.

Dan said...

That's me. What's your email address?

Gkon Electricals & Electronics Pvt. Ltd. said...
This comment has been removed by a blog administrator.
Gkon Electricals & Electronics Pvt. Ltd. said...

Gkon Electricals & Electronics Pvt. Ltd. - A renowmed switchgears manufacturers in India and one of the leading electrical switch gears manufacturers and suppliers in Delhi India.

Anonymous said...

I think you can just use the builtin "ff-find-other-file".

Anonymous said...

Unfortunately xemacs seems not to have a function like "ff-find-other-file", so this comes in very handy. Thank's Dan.