Subject: bug#28050: CC Mode 5.33 (C/*l); indentation of array and enum in GNU style isn't right

From: Alan Mackenzie <[email-masked]>
Date: Sat, 22 Sep 2018 19:30:59 +0000

Hello, Mohammed.

It's been over a year since you reported the bug below.  I'm top-posting
this, so that you see exactly what you wrote, it being so long ago.
Sorry about this delay.  Are you still interested in this bug?

This has been a difficult bug to solve, not for the cases you gave,
e.g.:

    typedef enum {
                  GOOD,
		  BETTER,
		  BEST
    };

but the case in which the first element inside the braces is on the same
line as the opening brace:

    typedef enum {GOOD,
        BETTER,
	BEST
    };

, which surely ought to be indented as above (I'm using a c-basic-offset
of 4 for clarity in the examples), and _not_ like this:

    typedef enum {GOOD,
                  BETTER,
		  BEST
    };

.  However, Emacs bug #24431 from Matthias Meulien, complained about the
exact opposite.  His case was that the following C++ was mis-aligned:

    set_line(line_t{point_t{0.4, 0.2},
          point_t{0.2, 0.5}}); // wrong identation

, and that the second line should line up with the "point_t" in the
first line.  I "fixed" this bug, hence the trouble with GOOD, BETTER,
and BEST.

My trouble with bug #24431 was I didn't realise that that was a special
case.  What makes it so was the opening paren after "set_line", a bit of
DWIM ("Do what I mean").

I think I've now (almost) got this sorted out.  I'm applying the
following rules to brace lists in "gnu" style and several other styles:

1. When the first entry is on the next line after the brace, it will be
indented by c-basic-offset ("+"), as will the following entries.

2. When the first entry is on the _same_ line as the brace, the
following entries are normally indented by c-basic-offset.

3. As an exception to 2., if the line with the opening brace and first
entry has a "(" or another "{" earlier on IN THE LINE, the second and
subsequent entries are lined up under the first entry:

    set_line(line_t{point_t{0.4, 0.2},
                    point_t{0.2, 0.5}});

4. As a further exception to 2, when a variable of a struct, union,
class, .... type is being declared like in the following example, the
second and subsequent entries are indented c-basic-offset from THE
VARIABLE, not from the start of the declaration:

1.    struct STR {
2.        int i; float f;
3.    } str_1 = /* ; */ {1, 1.7},
4.        str_2 = {2,
5.            3.1                  <================================
6.        };

Critical here is that the brace block of "struct STR" is present, and
one or more variables of this type are declared at the same place.  The
"3.1" on line 5 is indented from the "str_2" on line 4, NOT from the
anchor point which is "struct" on line 1.

However, the changes to implement these rules have produced a patch a
little under 600 lines long.  :-(  These changes are not quite finished:
some doc strings need writing, and some new line-up functions need to be
documented in the CC Mode manual.  However, other than that it is ready.

Are you still interested in this bug?  If so, would you be prepared to
apply this ~600 line patch, compile it, and test it?  If so, I would be
grateful.  :-)  If so 2, would you please tell me in your reply which CC
Mode sources you are using (I'm a bit sloppy with version numbers).  Are
you using the standalone CC Mode from sourceforge, or the version
distributed together with Emacs 26?  Knowing this would help me prepare
the patch for the version of CC Mode you're actually using.

-- 
Alan Mackenzie (Nuremberg, Germany).

On Fri, Aug 11, 2017 at 22:50:02 +0530, Mohammed Sadiq wrote:

> For the following code, the indentation of the code isn't right for GNU
> style (C-x h TAB after writing the code):

> /* Output after default (auto)indentation in GNU Emacs (TAB is used sometimes) */
> char *words[] = {
> 		 "good",
> 		 "better",
> 		 "best"
> };

> The GNU style is (as done by the program GNU indent):

> /* They should be indented with 2 spaces (and not TABs)
> char *words[] = {
>   "good",
>   "better",
>   "best"
> };

> Tabs are also add for indentation when defining enums:

> typedef enum {
> 	        GOOD,
> 	        BETTER,
> 	        BEST
> } test;

> The following might be better (2 spaces, and no TABs):

> typedef enum {
>   GOOD,
>   BETTER,
>   BEST
> } test;

> Emacs  : GNU Emacs 26.0.50 (build 1, x86_64-pc-linux-gnu, GTK+ Version 3.22.11)
>  of 2017-08-11
> Package: CC Mode 5.33 (C/*l)
> Buffer Style: gnu
> c-emacs-features: (pps-extended-state col-0-paren posix-char-classes gen-string-delim gen-comment-delim syntax-properties 1-bit)

> current state:
> ==============
> (setq
>  c-basic-offset 2
>  c-comment-only-line-offset '(0 . 0)
>  c-indent-comment-alist '((anchored-comment column . 0) (end-block space . 1)
> 			  (cpp-end-block space . 2))
>  c-indent-comments-syntactically-p nil
>  c-block-comment-prefix ""
>  c-comment-prefix-regexp '((pike-mode . "//+!?\\|\\**") (awk-mode . "#+")
> 			   (other . "//+\\|\\**"))
>  c-doc-comment-style '((java-mode . javadoc) (pike-mode . autodoc)
> 		       (c-mode . gtkdoc))
>  c-cleanup-list '(scope-operator)
>  c-hanging-braces-alist '((substatement-open before after)
> 			  (arglist-cont-nonempty))
>  c-hanging-colons-alist nil
>  c-hanging-semi&comma-criteria '(c-semi&comma-inside-parenlist)
>  c-backslash-column 48
>  c-backslash-max-column 72
>  c-special-indent-hook '(c-gnu-impose-minimum)
>  c-label-minimum-indentation 1
>  c-offsets-alist '((inexpr-class . +)
> 		   (inexpr-statement . +)
> 		   (lambda-intro-cont . +)
> 		   (inlambda . c-lineup-inexpr-block)
> 		   (template-args-cont c-lineup-template-args +)
> 		   (incomposition . +)
> 		   (inmodule . +)
> 		   (innamespace . +)
> 		   (inextern-lang . +)
> 		   (composition-close . 0)
> 		   (module-close . 0)
> 		   (namespace-close . 0)
> 		   (extern-lang-close . 0)
> 		   (composition-open . 0)
> 		   (module-open . 0)
> 		   (namespace-open . 0)
> 		   (extern-lang-open . 0)
> 		   (objc-method-call-cont
> 		    c-lineup-ObjC-method-call-colons
> 		    c-lineup-ObjC-method-call
> 		    +
> 		    )
> 		   (objc-method-args-cont . c-lineup-ObjC-method-args)
> 		   (objc-method-intro . [0])
> 		   (friend . 0)
> 		   (cpp-define-intro c-lineup-cpp-define +)
> 		   (cpp-macro-cont . +)
> 		   (cpp-macro . [0])
> 		   (inclass . +)
> 		   (stream-op . c-lineup-streamop)
> 		   (arglist-cont-nonempty
> 		    c-lineup-gcc-asm-reg
> 		    c-lineup-arglist
> 		    )
> 		   (arglist-cont c-lineup-gcc-asm-reg 0)
> 		   (comment-intro c-lineup-knr-region-comment c-lineup-comment)
> 		   (catch-clause . 0)
> 		   (else-clause . 0)
> 		   (do-while-closure . 0)
> 		   (access-label . -)
> 		   (case-label . 0)
> 		   (substatement . +)
> 		   (statement-case-intro . +)
> 		   (statement . 0)
> 		   (brace-entry-open . 0)
> 		   (brace-list-entry . c-lineup-under-anchor)
> 		   (brace-list-close . 0)
> 		   (block-close . 0)
> 		   (block-open . 0)
> 		   (inher-cont . c-lineup-multi-inher)
> 		   (inher-intro . +)
> 		   (member-init-cont . c-lineup-multi-inher)
> 		   (member-init-intro . +)
> 		   (annotation-var-cont . +)
> 		   (annotation-top-cont . 0)
> 		   (topmost-intro . 0)
> 		   (knr-argdecl . 0)
> 		   (func-decl-cont . +)
> 		   (inline-close . 0)
> 		   (class-close . 0)
> 		   (class-open . 0)
> 		   (defun-block-intro . +)
> 		   (defun-close . 0)
> 		   (defun-open . 0)
> 		   (c . c-lineup-C-comments)
> 		   (string . c-lineup-dont-change)
> 		   (topmost-intro-cont
> 		    first
> 		    c-lineup-topmost-intro-cont
> 		    c-lineup-gnu-DEFUN-intro-cont
> 		    )
> 		   (brace-list-intro . c-lineup-arglist-intro-after-paren)
> 		   (brace-list-open . +)
> 		   (inline-open . 0)
> 		   (arglist-close . c-lineup-arglist)
> 		   (arglist-intro . c-lineup-arglist-intro-after-paren)
> 		   (statement-cont . +)
> 		   (statement-case-open . +)
> 		   (label . 0)
> 		   (substatement-label . 0)
> 		   (substatement-open . +)
> 		   (knr-argdecl-intro . 5)
> 		   (statement-block-intro . +)
> 		   )
>  c-buffer-is-cc-mode 'c-mode
>  c-tab-always-indent t
>  c-syntactic-indentation t
>  c-syntactic-indentation-in-macros t
>  c-ignore-auto-fill '(string cpp code)
>  c-auto-align-backslashes t
>  c-backspace-function 'backward-delete-char-untabify
>  c-delete-function 'delete-char
>  c-electric-pound-behavior nil
>  c-default-style '((java-mode . "java") (awk-mode . "awk") (other . "gnu"))
>  c-enable-xemacs-performance-kludge-p nil
>  c-old-style-variable-behavior nil
>  defun-prompt-regexp nil
>  tab-width 8
>  comment-column 32
>  parse-sexp-ignore-comments t
>  parse-sexp-lookup-properties t
>  auto-fill-function nil
>  comment-multi-line t
>  comment-start-skip "\\(//+\\|/\\*+\\)\\s *"
>  fill-prefix nil
>  fill-column 70
>  paragraph-start "[ 	]*\\(//+\\|\\**\\)[ 	]*$\\|^\f"
>  adaptive-fill-mode t
>  adaptive-fill-regexp "[ 	]*\\(//+\\|\\**\\)[ 	]*\\([ 	]*\\([-–!|#%;>*·•‣⁃◦]+[ 	]*\\)*\\)"
>  )

> ------------------------------------------------------------------------------
> Check out the vibrant tech community on one of the world's most
> engaging tech sites, Slashdot.org! http://sdm.link/slashdot