C/C++/Java code indentation in Emacs

  |   Source

1 Problem

There are two styles when insert curly braces in C like languages.

Style 1:

if(true) {
    printf("hello world\n");

Style 2:

    printf("hello world\n");

Whatever style I use, I expect Emacs will properly handle the indentation for me.

In "Style 1", when I press ENTER key after "{" at first line, I expect the new line will indent four spaces.

In "Style 2", when I press ENTER key after ")" at first line, I expect the new line will NOT indent.

2 Solution

Insert below code into ~/.emacs:

(defun fix-c-indent-offset-according-to-syntax-context (key val)
  ;; remove the old element
  (setq c-offsets-alist (delq (assoc key c-offsets-alist) c-offsets-alist))
  ;; new value
  (add-to-list 'c-offsets-alist '(key . val)))

(add-hook 'c-mode-common-hook
          (lambda ()
            (when (derived-mode-p 'c-mode 'c++-mode 'java-mode)
              ;; indent
              (fix-c-indent-offset-according-to-syntax-context 'substatement 0)
              (fix-c-indent-offset-according-to-syntax-context 'func-decl-cont 0))

That's it.

3 Explanation

When you press the ENTER key, the function c-indent-line will be called.

That function will do some simple syntax analysis and decide current syntactic context..

It will use that syntactic context to look up a global variable c-offsets-alist and decide how many spaces the new line will indent.

For example, the context substatement corresponds to the code like below:

if(true) // press ENTER here

And the context func-decl-cont corresponds to:

void fn () //press ENTER here

4 Technical details

When you press ENTER key, the new line will be inserted. Then the function indent-according-to-mode will always be called

indent-according-to-mode will call function object indent-line-function if it's not nil.

In C/C++/Java, that object is actually c-indent-line.

c-indent-line is defined in /usr/share/emacs/24.3/lisp/progmodes/cc-cmds.el (I use Emacs 24.3 on Gentoo Linux).

In that function, just below the code line:

(setq c-syntactic-context (c-guess-basic-syntax))

You can insert log code as below:

(message "c-syntactic-context=%s" c-syntactic-context)

You will know the current syntactic context when you press ENTER key through the output of log code.

EmacsWiki says you can run command "c-set-offset", whose hot key is "C-x C-o", in order to "see the syntax at point". As I tested, it does not work as expected. My way may seem a little bit intrusive but is reliable.

For example, the context statement-cont corresponds to the use case like this:

int a=3, // press ENTER here

Please note syntax analysis in c-indent-line is turned on if and only if the global flag c-syntactic-indentation is true.

Thanks for chengyi for reporting the issue and suggesting the fix.

BTW, EmacsWiki has a section to discuss the indenting in C. You may not need it if you have read this article and can read the Emacs lisp code.

Comments powered by Disqus