2007年6月21日星期四

Benjamin Rutt's Emacs C development tips

Benjamin Rutt's Emacs C development tips

Benjamin Rutt's Emacs C development tips

Welcome

So you are developing in C (or C++, or java) in emacs. This web page will hopefully make you more productive at doing just that. It is based on my experience of using GNU Emacs as a development environment for C and syntactically similar languages since 1997. I'm assuming you use a relatively recent GNU Emacs (preferably version 21.x) on a UNIX-like machine. Most of my tips will probably also work in XEmacs in some other development environment, but your mileage may vary.

Preliminaries

You should add any customization code to your ~/.emacs. If you're doing any C customization at all, add a
(require 'cc-mode)
to your ~/.emacs. If you'd like syntax colorization, add a
(global-font-lock-mode 1)
to your ~/.emacs.

Compiling/linking your project

Emacs makes it easy to get your project to build, by providing a means to move the cursor (known as the "point" in emacs) to the locus of compilation errors. Simply run M-x compile, and then type in the name of your compilation command (this is probably "make" or something similar). If there are any errors, you can press the key sequence C-x ` to visit the first error found. You can then press C-x ` again to visit the second error found, etc. To re-start the sequence of visiting errors (i.e. to visit the first one again), use the key sequence C-u C-x ` which will take you back to the first error. At one point, I got tired of typing M-x compile, so I bound it to a key, in particular F9 (Borland JBuilder trained me that this key is for building and also keys F5-F9 are user-assignable in emacs). Here's how I did that:
(global-set-key [(f9)] 'compile)
Now, pressing F9 ENTER starts a build. Or, you can adjust the build command in between pressing F9 and ENTER.

Better management of the compilation window

I don't like it that the compilation window takes up 1/2 of the frame by default. So, I use the following, which works well most of the time:
(setq compilation-window-height 8)
I also don't like that the compilation window sticks around after a successful compile. After all, most of the time, all I care about is that the compile completed cleanly. Here's how I make the compilation window go away, only if there was no compilation errors:
(setq compilation-finish-function
(lambda (buf str)

(if (string-match "exited abnormally" str)

;;there were errors
(message "compilation errors, press C-x ` to visit")

;;no errors, make the compilation window go away in 0.5 seconds
(run-at-time 0.5 nil 'delete-windows-on buf)
(message "NO COMPILATION ERRORS!"))))

Enabling hungry delete

Simply add the line (c-toggle-hungry-state 1) to your ~/.emacs, and you will enable "hungry" delete, which means that all whitespace around the cursor will be consumed when you press Backspace or C-d. Try it out, it will probably save you some keystrokes.

Tabs vs. spaces for indentation

It's easy to indent or re-indent current lines when doing C development, just press the TAB key. The problem comes when you want to share your code with others. By default, emacs uses a mixture of tabs and spaces for indentation, since tabs save bytes (by representing multiple units of indentation with a single byte), and spaces are important for alignment. Not all editors use this strategy however, and may barf on code that is indented using this strategy. I personally like to use all-spaces for indentation and no TABs, but sometimes it's not your call how to indent your files. Here's how to play nicely with e.g coworkers who use different editors.

If you need to use only TABs for indentation

You can achieve this in C/C++/java modes with the following in your ~/.emacs:
(require 'cc-mode)
(defun my-build-tab-stop-list (width)
(let ((num-tab-stops (/ 80 width))
(counter 1)
(ls nil))
(while (<= counter num-tab-stops)
(setq ls (cons (* width counter) ls))
(setq counter (1+ counter)))
(set (make-local-variable 'tab-stop-list) (nreverse ls))))
(defun my-c-mode-common-hook ()
(setq tab-width 5) ;; change this to taste, this is what K&R uses :)
(my-build-tab-stop-list tab-width)
(setq c-basic-offset tab-width))
(add-hook 'c-mode-common-hook 'my-c-mode-common-hook)
By redefining the tab-stop-list in each C (or similar) buffer inside a hook which will be executed as each C buffer is opened, indentations should arrive at tab stops, obviating the need for space-padding of any kind. This will not work correctly, however, if you are trying to lineup arglists, etc. (e.g. (c-set-offset 'arglist-cont-nonempty 'c-lineup-arglist)). That's a fundamental problem...how do you align code with TABS to arbitrary columns, without using spaces? Therefore, I recommend using only spaces for indentation instead...

If you need to use only spaces for indentation

You can achieve this in C/C++/java modes with the following in your ~/.emacs:
(require 'cc-mode)
(defun my-build-tab-stop-list (width)
(let ((num-tab-stops (/ 80 width))
(counter 1)
(ls nil))
(while (<= counter num-tab-stops)
(setq ls (cons (* width counter) ls))
(setq counter (1+ counter)))
(set (make-local-variable 'tab-stop-list) (nreverse ls))))
(defun my-c-mode-common-hook ()
(setq tab-width 5) ;; change this to taste, this is what K&R uses :)
(my-build-tab-stop-list tab-width)
(setq c-basic-offset tab-width)
(setq indent-tabs-mode nil)) ;; force only spaces for indentation
(add-hook 'c-mode-common-hook 'my-c-mode-common-hook)
By adding the line with the comment "force only spaces for indentation" to the earlier example, all indentations of new C code will use spaces for indentation. This will make life easier, as your code will look the same in all editors and you can explore using spaces for lining up argument lists, etc.

Customizing indentation

First of all, put (require 'cc-mode) atop the C customization section of your ~/.emacs, so the methods below will be defined. Then, while visiting a C source file, if you don't like how a particular line is indenting, press C-c C-o near the expression you want to change, and figure out the symbol to change (pressing C-c C-o gives you a chance to set these variables interactively). Then, do a (c-set-offset 'symbol-to-change X) in your ~/.emacs where X is typically a numeric value or the symbol '+ or '-. The following is an example of some of my customizations, which is simply my personal preference:
;; personal preferences
(c-set-offset 'substatement-open 0)
(c-set-offset 'case-label '+)
(c-set-offset 'arglist-cont-nonempty '+)
(c-set-offset 'arglist-intro '+)
(c-set-offset 'topmost-intro-cont '+)

Debugging in emacs

M-x gdb (or M-x jdb) is very useful to debug inside emacs. Try it, it will split your frame into two windows, with the gdb interaction buffer in the top window and the current source line (if the program is being executed) displayed in the bottom window. I typically use the following sequence:
  1. type M-x gdb. Enter the executable name to run, e.g. ./foobar
  2. in the gdb interaction buffer, type "break main" and "run"
  3. to use the debugger to execute the current statement and go to the next one, use C-c C-n
  4. to use the debugger to step into the current statement, use C-c C-s
You can still use all the power of gdb that you could from any shell prompt, but the arrow following the current source line is value added by emacs, providing debugger state without requiring a lot of gdb list statements.

Indexing your source code for easy navigation

Emacs comes with an etags executable (and also probably its cousin ctags), which can index source code, allowing you to jump around your source code quickly by simply giving a method name to jump to, etc. Here are some simple steps to get you started:
  1. from a shell, run etags *.[ch] in your source directory, which will index or re-index the .c and .h files in your source directory, and will create a file named TAGS.
  2. inside emacs, run M-x visit-tags-table RET and then enter the name of the TAGS file created by the previous step.
  3. for example, to visit a function named "sortRecords", type M-. and then enter the name "sortRecords". Or, better yet, enter just "sortRec", as substrings will work as well, as long as they are unique.
  4. did your press of M-. not put you in the right function/variable? Then try pressing C-u M-. to visit the next function/variable with a similar name.
  5. do you want to go back to where you were before the last M-. or C-u M-.? Then simply to M-*, each press of which will pop a stack and return you to where you came from.

没有评论: