From 35be59cc7b8d39f91b70aa57eaa09dc9b4636806 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Wed, 13 Aug 2025 07:21:09 +0800 Subject: [PATCH] vim-patch:9.1.1626: cindent: does not handle compound literals (#35319) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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: vim/vim#2090 fixes: vim/vim#12491 closes: vim/vim#17865 https://github.com/vim/vim/commit/5ba6e41d37ec29fc8360a4f06d4e95c63eb013ab Co-authored-by: Anttoni Erkkilä --- scripts/vim-patch.sh | 4 ++ src/nvim/indent_c.c | 108 +++++++++++++++++++++++-------- test/old/testdir/test_indent.vim | 25 +++++++ 3 files changed, 111 insertions(+), 26 deletions(-) diff --git a/scripts/vim-patch.sh b/scripts/vim-patch.sh index 77c7553612..8026e8ff09 100755 --- a/scripts/vim-patch.sh +++ b/scripts/vim-patch.sh @@ -262,6 +262,10 @@ preprocess_patch() { LC_ALL=C sed -Ee 's/( [ab]\/src\/nvim)\/evalwindow\.c/\1\/eval\/window.c/g' \ "$file" > "$file".tmp && mv "$file".tmp "$file" + # Rename cindent.c to indent_c.c + LC_ALL=C sed -Ee 's/( [ab]\/src\/nvim)\/cindent\.c/\1\/indent_c.c/g' \ + "$file" > "$file".tmp && mv "$file".tmp "$file" + # Rename map.c to mapping.c LC_ALL=C sed -Ee 's/( [ab]\/src\/nvim)\/map\.c/\1\/mapping.c/g' \ "$file" > "$file".tmp && mv "$file".tmp "$file" diff --git a/src/nvim/indent_c.c b/src/nvim/indent_c.c index 345d1a7720..b1bc5b3dc6 100644 --- a/src/nvim/indent_c.c +++ b/src/nvim/indent_c.c @@ -410,10 +410,76 @@ static bool cin_islabel(void) // XXX return true; // label at start of file??? } -// Recognize structure initialization and enumerations: -// "[typedef] [static|public|protected|private] enum" -// "[typedef] [static|public|protected|private] = {" -static int cin_isinit(void) +/// Strings can be concatenated with comments between: +/// "string0" |*comment*| "string1" +static const char *cin_skip_comment_and_string(const char *s) +{ + const char *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 bool cin_is_compound_init(const char *s) +{ + const char *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" +/// Calls another function to recognize structure initialization. +static bool cin_isinit(void) { const char *s; static char *skip[] = { "static", "public", "protected", "private" }; @@ -444,11 +510,7 @@ static int cin_isinit(void) return true; } - if (cin_ends_in(s, "=", "{")) { - return true; - } - - return false; + return cin_is_compound_init(s); } /// Recognize a switch label: "case .*:" or "default:". @@ -1299,7 +1361,7 @@ static int get_baseclass_amount(int col) && (trypos = find_match_paren(curbuf->b_ind_maxparen)) != NULL) { amount = get_indent_lnum(trypos->lnum); // XXX } - if (!cin_ends_in(get_cursor_line_ptr(), ",", NULL)) { + if (!cin_ends_in(get_cursor_line_ptr(), ",")) { amount += curbuf->b_ind_cpp_baseclass; } } else { @@ -1315,8 +1377,7 @@ static int get_baseclass_amount(int col) /// Return true if string "s" ends with the string "find", possibly followed by /// white space and comments. Skip strings and comments. -/// Ignore "ignore" after "find" if it's not NULL. -static int cin_ends_in(const char *s, const char *find, const char *ignore) +static int cin_ends_in(const char *s, const char *find) { const char *p = s; const char *r; @@ -1326,9 +1387,6 @@ static int cin_ends_in(const char *s, const char *find, const char *ignore) p = cin_skipcomment(p); if (strncmp(p, find, (size_t)len) == 0) { r = skipwhite(p + len); - if (ignore != NULL && strncmp(r, ignore, strlen(ignore)) == 0) { - r = skipwhite(r + strlen(ignore)); - } if (cin_nocode(r)) { return true; } @@ -2264,8 +2322,7 @@ int get_c_indent(void) if (theline[0] != ')') { cur_amount = MAXCOL; l = ml_get(our_paren_pos.lnum); - if (curbuf->b_ind_unclosed_wrapped - && cin_ends_in(l, "(", NULL)) { + if (curbuf->b_ind_unclosed_wrapped && cin_ends_in(l, "(")) { // look for opening unmatched paren, indent one level // for each additional level n = 1; @@ -3393,8 +3450,8 @@ term_again: && !cin_nocode(theline) && vim_strchr(theline, '{') == NULL && vim_strchr(theline, '}') == NULL - && !cin_ends_in(theline, ":", NULL) - && !cin_ends_in(theline, ",", NULL) + && !cin_ends_in(theline, ":") + && !cin_ends_in(theline, ",") && cin_isfuncdecl(NULL, cur_curpos.lnum + 1, cur_curpos.lnum + 1) && !cin_isterminated(theline, false, true)) { amount = curbuf->b_ind_func_type; @@ -3451,7 +3508,7 @@ term_again: // ... // } foo, // bar; - if (cin_ends_in(l, ",", NULL) + if (cin_ends_in(l, ",") || (*l != NUL && (n = (uint8_t)l[strlen(l) - 1]) == '\\')) { // take us back to opening paren if (find_last_paren(l, '(', ')') @@ -3502,7 +3559,7 @@ term_again: // comments) align at column 0. For example: // char *string_array[] = { "foo", // / * x * / "b};ar" }; / * foobar * / - if (cin_ends_in(l, "};", NULL)) { + if (cin_ends_in(l, "};")) { break; } @@ -3510,7 +3567,7 @@ term_again: // array constant: // something = [ // 234, <- extra indent - if (cin_ends_in(l, "[", NULL)) { + if (cin_ends_in(l, "[")) { amount = get_indent() + ind_continuation; break; } @@ -3528,8 +3585,7 @@ term_again: break; } } - if (curwin->w_cursor.lnum > 0 - && cin_ends_in(look, "}", NULL)) { + if (curwin->w_cursor.lnum > 0 && cin_ends_in(look, "}")) { break; } @@ -3549,9 +3605,9 @@ term_again: // int foo, // bar; // indent_to_0 here; - if (cin_ends_in(l, ";", NULL)) { + if (cin_ends_in(l, ";")) { l = ml_get(curwin->w_cursor.lnum - 1); - if (cin_ends_in(l, ",", NULL) + if (cin_ends_in(l, ",") || (*l != NUL && l[strlen(l) - 1] == '\\')) { break; } diff --git a/test/old/testdir/test_indent.vim b/test/old/testdir/test_indent.vim index 5767890b08..241644f201 100644 --- a/test/old/testdir/test_indent.vim +++ b/test/old/testdir/test_indent.vim @@ -122,6 +122,31 @@ func Test_userlabel_indent() close! 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' func Test_copyindent() new