Archive for the ‘Emacs’ Category

dot-emacs: smarter indentation with tabs and spaces

In XMMS2, the coding guidelines require you to use a mix of tabs and spaces to indent code: tabs indent blocks, while spaces are used to align multi-line statements. It’s all very natural really:

[tab][tab][tab]foo_function (arg1,
[tab][tab][tab]              arg2,
[tab][tab][tab]              arg3)

Unfortunately, if you use the default C indentation mode in emacs, what you get (when breaking like with C-j or RET TAB) is the following:

[tab][tab][tab]foo_function (arg1,
[tab][tab][tab][tab][tab]    arg2,
[tab][tab][tab][tab][tab]    arg3)

So you mean to tweak the code yourself to fix the tabs. Tedious, annoying. We know better!

I wrote a hook to fix this. The idea is to fix tabs when we’re in an argument list context. It can probably be further improved (please send patches!), but it’s quite useful already.

Just add the following to your .emacs file.

(defun xmms2-c-mode ()
  "C mode with adjusted defaults for use with the xmms2."
  (interactive)
  (c-mode)
  (c-set-style "K&R")
  (setq tab-width 4)
  (setq indent-tabs-mode t)
  (setq c-basic-offset 4)

  ; Align closing paren with opening paren
  (c-set-offset ‘arglist-close ‘c-lineup-arglist-intro-after-paren)

  (add-hook ‘c-special-indent-hook ’smart-tab-indent-hook))

(defun get-nonempty-context ()
  (let ((curr-context (car (c-guess-basic-syntax))))
    (if (or (eq (car curr-context) ‘arglist-intro)
            (eq (car curr-context) ‘arglist-cont)
            (eq (car curr-context) ‘arglist-cont-nonempty)
            (eq (car curr-context) ‘arglist-close))
        curr-context
      nil)))

(defun smart-tab-indent-hook ()
  "Fixes indentation to pad with spaces in arglists."
  (let ((nonempty-ctx (get-nonempty-context)))
    (if nonempty-ctx
        (let ((tabbed-columns (+ (point-at-bol)
                                 (/ (c-langelem-col nonempty-ctx t)
                                    tab-width)))
              (orig-column (current-column)))
          (tabify (point-at-bol) tabbed-columns)
          (untabify tabbed-columns (point-at-eol))
          ; editing tabs screws the pointer position
          (move-to-column orig-column)))))

Note: don’t forget the

c-set-offset

bit, else it won’t work properly for the last argument before the closing parenthesis. And avoid using

c-indent-new-comment-line

to break lines, it can fail with this hook; use

c-context-line-break

instead, it’s smarter anyway!