patch 9.1.1626: cindent: does not handle compound literals

Problem:  C-indent does not handle compound literals
          (@44100hertz, @Jorenar)
Solution: Detect and handle compound literal and structure
          initialization (Anttoni Erkkilä)

match '=' or "return" optionally followed by &, (typecast), {
Fixes also initialization which begins with multiple opening braces.

fixes: #2090
fixes: #12491
closes: #17865

Signed-off-by: Anttoni Erkkilä <anttoni.erkkila@protonmail.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
This commit is contained in:
Anttoni Erkkilä
2025-08-12 21:59:06 +02:00
committed by Christian Brabandt
parent 59e1d7f353
commit 5ba6e41d37
4 changed files with 111 additions and 21 deletions

View File

@ -1,4 +1,4 @@
*version9.txt* For Vim version 9.1. Last change: 2025 Aug 08 *version9.txt* For Vim version 9.1. Last change: 2025 Aug 12
VIM REFERENCE MANUAL by Bram Moolenaar VIM REFERENCE MANUAL by Bram Moolenaar
@ -41736,6 +41736,7 @@ Others: ~
- |gv| works in operator pending mode and does not abort - |gv| works in operator pending mode and does not abort
- The close button shown in the non-GUI 'tabline' will only be visible if the - The close button shown in the non-GUI 'tabline' will only be visible if the
'mouse' option contains either "a" or any of the flags "n", "v", or "i". 'mouse' option contains either "a" or any of the flags "n", "v", or "i".
- |C-indenting| handles compound literals.
*added-9.2* *added-9.2*
Added ~ Added ~

View File

@ -690,10 +690,9 @@ cin_islabel(void) // XXX
/* /*
* Return TRUE if string "s" ends with the string "find", possibly followed by * Return TRUE if string "s" ends with the string "find", possibly followed by
* white space and comments. Skip strings and comments. * white space and comments. Skip strings and comments.
* Ignore "ignore" after "find" if it's not NULL.
*/ */
static int static int
cin_ends_in(char_u *s, char_u *find, char_u *ignore) cin_ends_in(char_u *s, char_u *find)
{ {
char_u *p = s; char_u *p = s;
char_u *r; char_u *r;
@ -705,8 +704,6 @@ cin_ends_in(char_u *s, char_u *find, char_u *ignore)
if (STRNCMP(p, find, len) == 0) if (STRNCMP(p, find, len) == 0)
{ {
r = skipwhite(p + len); r = skipwhite(p + len);
if (ignore != NULL && STRNCMP(r, ignore, STRLEN(ignore)) == 0)
r = skipwhite(r + STRLEN(ignore));
if (cin_nocode(r)) if (cin_nocode(r))
return TRUE; return TRUE;
} }
@ -717,9 +714,77 @@ cin_ends_in(char_u *s, char_u *find, char_u *ignore)
} }
/* /*
* Recognize structure initialization and enumerations: * Strings can be concatenated with comments between:
* "string0" |*comment*| "string1"
*/
static char_u *
cin_skip_comment_and_string(char_u *s)
{
char_u *r = NULL, *p = s;
do
{
r = p;
p = cin_skipcomment(p);
if (*p)
p = skip_string(p);
} while (p != r);
return p;
}
/*
* Recognize structure or compound literal initialization:
* =|return [&][(typecast)] [{]
* The number of opening braces is arbitrary.
*/
static int
cin_is_compound_init(char_u *s)
{
char_u *p = s, *r = NULL;
while (*p)
{
if (*p == '=')
p = r = cin_skipcomment(p + 1);
else if (!STRNCMP(p, "return", 6) && !vim_isIDc(p[6])
&& (p == s || (p > s && !vim_isIDc(p[-1]))))
p = r = cin_skipcomment(p + 6);
else
p = cin_skip_comment_and_string(p + 1);
}
if (!r)
return FALSE;
p = r; // p points now after '=' or "return"
if (cin_nocode(p))
return TRUE;
if (*p == '&')
p = cin_skipcomment(p + 1);
if (*p == '(') // skip a typecast
{
int open_count = 1;
do
{
p = cin_skip_comment_and_string(p + 1);
if (cin_nocode(p))
return TRUE;
open_count += (*p == '(') - (*p == ')');
} while (open_count);
p = cin_skipcomment(p + 1);
if (cin_nocode(p))
return TRUE;
}
while (*p == '{')
p = cin_skipcomment(p + 1);
return cin_nocode(p);
}
/*
* Recognize enumerations:
* "[typedef] [static|public|protected|private] enum" * "[typedef] [static|public|protected|private] enum"
* "[typedef] [static|public|protected|private] = {" * Call another function to recognize structure initialization.
*/ */
static int static int
cin_isinit(void) cin_isinit(void)
@ -753,10 +818,7 @@ cin_isinit(void)
if (cin_starts_with(s, "enum")) if (cin_starts_with(s, "enum"))
return TRUE; return TRUE;
if (cin_ends_in(s, (char_u *)"=", (char_u *)"{")) return cin_is_compound_init(s);
return TRUE;
return FALSE;
} }
// Maximum number of lines to search back for a "namespace" line. // Maximum number of lines to search back for a "namespace" line.
@ -1634,7 +1696,7 @@ get_baseclass_amount(int col)
if (find_last_paren(ml_get_curline(), '(', ')') if (find_last_paren(ml_get_curline(), '(', ')')
&& (trypos = find_match_paren(curbuf->b_ind_maxparen)) != NULL) && (trypos = find_match_paren(curbuf->b_ind_maxparen)) != NULL)
amount = get_indent_lnum(trypos->lnum); // XXX amount = get_indent_lnum(trypos->lnum); // XXX
if (!cin_ends_in(ml_get_curline(), (char_u *)",", NULL)) if (!cin_ends_in(ml_get_curline(), (char_u *)","))
amount += curbuf->b_ind_cpp_baseclass; amount += curbuf->b_ind_cpp_baseclass;
} }
else else
@ -2505,7 +2567,7 @@ get_c_indent(void)
cur_amount = MAXCOL; cur_amount = MAXCOL;
l = ml_get(our_paren_pos.lnum); l = ml_get(our_paren_pos.lnum);
if (curbuf->b_ind_unclosed_wrapped if (curbuf->b_ind_unclosed_wrapped
&& cin_ends_in(l, (char_u *)"(", NULL)) && cin_ends_in(l, (char_u *)"("))
{ {
// look for opening unmatched paren, indent one level // look for opening unmatched paren, indent one level
// for each additional level // for each additional level
@ -3702,8 +3764,8 @@ term_again:
&& !cin_nocode(theline) && !cin_nocode(theline)
&& vim_strchr(theline, '{') == NULL && vim_strchr(theline, '{') == NULL
&& vim_strchr(theline, '}') == NULL && vim_strchr(theline, '}') == NULL
&& !cin_ends_in(theline, (char_u *)":", NULL) && !cin_ends_in(theline, (char_u *)":")
&& !cin_ends_in(theline, (char_u *)",", NULL) && !cin_ends_in(theline, (char_u *)",")
&& cin_isfuncdecl(NULL, cur_curpos.lnum + 1, && cin_isfuncdecl(NULL, cur_curpos.lnum + 1,
cur_curpos.lnum + 1) cur_curpos.lnum + 1)
&& !cin_isterminated(theline, FALSE, TRUE)) && !cin_isterminated(theline, FALSE, TRUE))
@ -3764,7 +3826,7 @@ term_again:
// } foo, // } foo,
// bar; // bar;
n = 0; n = 0;
if (cin_ends_in(l, (char_u *)",", NULL) if (cin_ends_in(l, (char_u *)",")
|| (*l != NUL && (n = l[STRLEN(l) - 1]) == '\\')) || (*l != NUL && (n = l[STRLEN(l) - 1]) == '\\'))
{ {
// take us back to opening paren // take us back to opening paren
@ -3812,14 +3874,14 @@ term_again:
// comments) align at column 0. For example: // comments) align at column 0. For example:
// char *string_array[] = { "foo", // char *string_array[] = { "foo",
// / * x * / "b};ar" }; / * foobar * / // / * x * / "b};ar" }; / * foobar * /
if (cin_ends_in(l, (char_u *)"};", NULL)) if (cin_ends_in(l, (char_u *)"};"))
break; break;
// If the previous line ends on '[' we are probably in an // If the previous line ends on '[' we are probably in an
// array constant: // array constant:
// something = [ // something = [
// 234, <- extra indent // 234, <- extra indent
if (cin_ends_in(l, (char_u *)"[", NULL)) if (cin_ends_in(l, (char_u *)"["))
{ {
amount = get_indent() + ind_continuation; amount = get_indent() + ind_continuation;
break; break;
@ -3840,7 +3902,7 @@ term_again:
break; break;
} }
if (curwin->w_cursor.lnum > 0 if (curwin->w_cursor.lnum > 0
&& cin_ends_in(look, (char_u *)"}", NULL)) && cin_ends_in(look, (char_u *)"}"))
break; break;
curwin->w_cursor = curpos_save; curwin->w_cursor = curpos_save;
@ -3860,10 +3922,10 @@ term_again:
// int foo, // int foo,
// bar; // bar;
// indent_to_0 here; // indent_to_0 here;
if (cin_ends_in(l, (char_u *)";", NULL)) if (cin_ends_in(l, (char_u *)";"))
{ {
l = ml_get(curwin->w_cursor.lnum - 1); l = ml_get(curwin->w_cursor.lnum - 1);
if (cin_ends_in(l, (char_u *)",", NULL) if (cin_ends_in(l, (char_u *)",")
|| (*l != NUL && l[STRLEN(l) - 1] == '\\')) || (*l != NUL && l[STRLEN(l) - 1] == '\\'))
break; break;
l = ml_get_curline(); l = ml_get_curline();

View File

@ -119,6 +119,31 @@ func Test_userlabel_indent()
close! close!
endfunc endfunc
" Test that struct members are aligned
func Test_struct_indent()
new
call setline(1, ['struct a a = {', '1,', '1,'])
normal gg=G
call assert_equal(getline(2), getline(3))
call setline(1, 'a = (struct a) {')
normal gg=G
call assert_equal(getline(2), getline(3))
call setline(1, 'void *ptr = &(static struct a) {{')
normal gg=G
call assert_equal(getline(2), getline(3))
call setline(1, 'a = (macro(arg1, "str)))")) {')
normal gg=G
call assert_equal(getline(2), getline(3))
call setline(1, 'return (struct a) {')
normal gg=G
call assert_equal(getline(2), getline(3))
close!
endfunc
" Test for 'copyindent' " Test for 'copyindent'
func Test_copyindent() func Test_copyindent()
new new

View File

@ -719,6 +719,8 @@ static char *(features[]) =
static int included_patches[] = static int included_patches[] =
{ /* Add new patch number below this line */ { /* Add new patch number below this line */
/**/
1626,
/**/ /**/
1625, 1625,
/**/ /**/