Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

emacs: different indentation for class and struct

I'm trying to achieve the following indentation in emacs:

class A
{
    // I ALWAYS use access labels in classes

    public: // access-label
        int member; // inclass
};

struct B
{
    // I NEVER use access labels in structs

    int member; // inclass
};

However with the following configuration file...

(defun my-cpp-mode ()
  "My C++ mode"
  (c++-mode)
  (c-set-style "K&R")
  (setq c-basic-offset 4)
  (c-set-offset 'access-label '-)
  (c-set-offset 'inclass '++)
  ;; ...
  (setq mode-name "My C++")
)
(add-to-list 'auto-mode-alist '("\\.[ch]p?p?\\'" . my-cpp-mode))

... I achieve only:

class A
{
    public: // access-label
        int member; // inclass
};

struct B
{
        // this indentation is too long
        int member; // inclass
};

Of course that's because:

  • for the indentation there is obviously no difference between "class" and "struct" (it's all "inclass"),
  • the indentation of "inclass" stuff doesn't depend on the presence of access labels or not.

Any idea how I can make the indentation of inclass stuff dependent on either class/struct or on the presence of access labels?

like image 864
Aurelien Avatar asked Feb 05 '13 19:02

Aurelien


2 Answers

New Answer

I ran into the exact requirement that you had mentioned in your question. I had to setup indentation according to the coding style of my new project. After a bit of research, I achieved this using Custom Line-up Functions.

Modify your my-cpp-mode to look like this:

(defun my-c-lineup-inclass (langelem)
  (let ((inclass (assoc 'inclass c-syntactic-context)))
    (save-excursion
      (goto-char (c-langelem-pos inclass))
      (if (or (looking-at "struct")
              (looking-at "typedef struct"))
          '+
        '++))))

(defun my-cpp-mode ()
  "My C++ mode"
  (c++-mode)
  (c-set-style "K&R")
  (setq c-basic-offset 4)
  (c-set-offset 'access-label '-)
  (c-set-offset 'inclass 'my-c-lineup-inclass)
  ;; ...
  (setq mode-name "My C++")
)

If this answer is acceptable, I'll go ahead and remove the old answer.

Old Answer

Based on what you are trying to achieve, may I suggest a different approach? You seem to want the access label at a different indentation level than the class and the class members. Use the following to achieve that.

(access-label . /)

From Emacs documentation:

If OFFSET is one of the symbols +',-', ++',--', *', or/' then a positive or negative multiple of `c-basic-offset' is added to the base indentation; 1, -1, 2, -2, 0.5, and -0.5, respectively.

Here is a snippet from one of the custom styles that I have defined.

(c-add-style
 "xyz-style"
 '((indent-tabs-mode . nil)
   (fill-column . 75)
   (c-basic-offset . 4)
   (c-offsets-alist . (
                       (access-label . /)
                       (inextern-lang . 0)
                       (innamespace . 0)
                       (member-init-intro . ++)
                       ))))

With c-basic-offset set to 4, (access-label . /) adds a negative indentation of 2 spaces to the access labels. Here is the actual result of my indentation mode on your sample code.

class A
{
    // I ALWAYS use access labels in classes

  public: // access-label
    int member; // inclass
};

struct B
{
    // I NEVER use access labels in structs

    int member; // inclass
};

I recommend this mode because, the indentation level of the member variables/struct members is consistent. FWIW, Google C Style follows the same approach.

As far as I can tell, one cannot differentiate between a class member or a struct member (inclass sytax element). You can use M-x c-syntactic-information-on-region to do a syntactic analysis on a region. One such analysis on you example yields the following. From the output, there is nothing to differentiate between if you are in a class or a struct.

class A                                 // ((topmost-intro 1))
{                                       // ((class-open 1))
                                        // ((inclass 64) (topmost-intro 64) (comment-intro))I ALWAYS use access labels in classes
                                        // ((inclass 64) (topmost-intro 64))
  public:                               // ((inclass 64) (access-label 64))access-label
    int member;                         // ((inclass 64) (topmost-intro 64))inclass
};                                      // ((class-close 1))
                                        // ((topmost-intro 503))
struct B                                // ((topmost-intro 503))
{                                       // ((class-open 629))
                                        // ((inclass 694) (topmost-intro 694) (comment-intro))I NEVER use access labels in structs
                                        // ((inclass 694) (topmost-intro 694))
    int member;                         // ((inclass 694) (topmost-intro 694))inclass
};                                      // ((class-close 629))
like image 134
Praveen Kumar Avatar answered Nov 18 '22 01:11

Praveen Kumar


Based on Praveen Kumar's answer above, I implemented a slightly different version of the custom line-up function:

(defun my-c-lineup-inclass (langelem)
  (let ((inclass (assoc 'inclass c-syntactic-context)))
    (save-excursion
      (c-beginning-of-defun) ; This sees the correct string.
      (if (or (looking-at "struct")
              (looking-at "typedef struct"))
          '+
        '++))))

; In particular, the following offsets need to be specified:
(c-set-offset 'access-label '-)
(c-set-offset 'inclass 'my-c-lineup-inclass)
; ...

The original code did not work in case the brace was on the next line, i.e.

struct foo
{
        int bar;
};

would still indent to "++".

Disclaimer: I don't know any Lisp. I just played around and this works for me. I don't know, e.g., if there are any performance issues associated with this.

like image 2
Michael L. Flegel Avatar answered Nov 18 '22 03:11

Michael L. Flegel