vim-patch:9.2.0447: cindent does not ignore comments (#39622)

Problem:  When find_start_brace() scans backwards for the enclosing
          block, '{' and '}' inside // and /* */ comments are counted,
          producing wrong indent for code following such comments
          (rendcrx).
Solution: Implement FM_SKIPCOMM in findmatchlimit() to track block-
          comment state and skip matches inside comments. Pass
          FM_SKIPCOMM from cindent's call sites
          (find_start_brace, find_match_char, cin_iswhileofdo,
          get_c_indent).

fixes:  vim/vim#4
fixes:  vim/vim#648
fixes:  vim/vim#19578
closes: vim/vim#19581
closes: vim/vim#20111

https://github.com/vim/vim/commit/c06002f3cb07c374bfc1a1310cf13ee1914e86da

Co-authored-by: magnus-rattlehead <magnus-rattlehead@users.noreply.github.com>
This commit is contained in:
zeertzjq
2026-05-06 09:45:18 +08:00
committed by GitHub
parent cfa803d895
commit d9a7b68795
4 changed files with 139 additions and 12 deletions
+4 -4
View File
@@ -1079,7 +1079,7 @@ static int cin_iswhileofdo(const char *p, linenr_T lnum) // XXX
p++;
curwin->w_cursor.col++;
}
if ((trypos = findmatchlimit(NULL, 0, 0, curbuf->b_ind_maxparen)) != NULL
if ((trypos = findmatchlimit(NULL, 0, FM_SKIPCOMM, curbuf->b_ind_maxparen)) != NULL
&& *cin_skipcomment(ml_get_pos(trypos) + 1) == ';') {
retval = true;
}
@@ -1484,7 +1484,7 @@ static pos_T *find_start_brace(void) // XXX
static pos_T pos_copy;
cursor_save = curwin->w_cursor;
while ((trypos = findmatchlimit(NULL, '{', FM_BLOCKSTOP, 0)) != NULL) {
while ((trypos = findmatchlimit(NULL, '{', FM_BLOCKSTOP | FM_SKIPCOMM, 0)) != NULL) {
pos_copy = *trypos; // copy pos_T, next findmatch will change it
trypos = &pos_copy;
curwin->w_cursor = *trypos;
@@ -1519,7 +1519,7 @@ static pos_T *find_match_char(char c, int ind_maxparen)
cursor_save = curwin->w_cursor;
ind_maxp_wk = ind_maxparen;
retry:
if ((trypos = findmatchlimit(NULL, (uint8_t)c, 0, ind_maxp_wk)) != NULL) {
if ((trypos = findmatchlimit(NULL, (uint8_t)c, FM_SKIPCOMM, ind_maxp_wk)) != NULL) {
// check if the ( is in a // comment
if ((colnr_T)cin_skip2pos(trypos) > trypos->col) {
ind_maxp_wk = ind_maxparen - (cursor_save.lnum - trypos->lnum);
@@ -2299,7 +2299,7 @@ int get_c_indent(void)
line = get_cursor_line_ptr();
look_col = (int)(look - line);
curwin->w_cursor.col = look_col + 1;
if ((trypos = findmatchlimit(NULL, ')', 0,
if ((trypos = findmatchlimit(NULL, ')', FM_SKIPCOMM,
curbuf->b_ind_maxparen))
!= NULL
&& trypos->lnum == our_paren_pos.lnum
+54 -7
View File
@@ -1763,10 +1763,10 @@ static void find_mps_values(int *initc, int *findc, bool *backwards, bool switch
// '#' look for preprocessor directives
// 'R' look for raw string start: R"delim(text)delim" (only backwards)
//
// flags: FM_BACKWARD search backwards (when initc is '/', '*' or '#')
// FM_FORWARD search forwards (when initc is '/', '*' or '#')
// FM_BLOCKSTOP stop at start/end of block ({ or } in column 0)
// FM_SKIPCOMM skip comments (not implemented yet!)
// flags: FM_BACKWARD search backwards (when initc is '/', '*' or '#')
// FM_FORWARD search forwards (when initc is '/', '*' or '#')
// FM_BLOCKSTOP stop at start/end of block ({ or } in column 0)
// FM_SKIPCOMM skip over comments (cursor must start outside a block comment)
//
// "oap" is only used to set oap->motion_type for a linewise motion, it can be
// NULL
@@ -1788,6 +1788,8 @@ pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel)
int comment_col = MAXCOL; // start of / / comment
bool lispcomm = false; // inside of Lisp-style comment
bool lisp = curbuf->b_p_lisp; // engage Lisp-specific hacks ;)
bool skip_comments = (flags & FM_SKIPCOMM) != 0;
bool in_block_comment = false; // inside / * * / block comment
pos = curwin->w_cursor;
pos.coladd = 0;
@@ -1985,12 +1987,17 @@ pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel)
clearpos(&match_pos);
// backward search: Check if this line contains a single-line comment
if ((backwards && comment_dir) || lisp) {
if ((backwards && comment_dir) || lisp || skip_comments) {
comment_col = check_linecomment(linep);
}
if (lisp && comment_col != MAXCOL && pos.col > (colnr_T)comment_col) {
lispcomm = true; // find match inside this comment
}
// skip // comment portion at starting position
if (skip_comments && !in_block_comment && comment_col != MAXCOL
&& backwards && pos.col > (colnr_T)comment_col) {
pos.col = comment_col;
}
while (!got_int) {
// Go to the next position, forward or backward. We could use
@@ -2016,12 +2023,16 @@ pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel)
line_breakcheck();
// Check if this line contains a single-line comment
if (comment_dir || lisp) {
if (comment_dir || lisp || skip_comments) {
comment_col = check_linecomment(linep);
}
// skip comment
if (lisp && comment_col != MAXCOL) {
pos.col = comment_col;
} else if (skip_comments && !in_block_comment
&& comment_col != MAXCOL
&& pos.col > (colnr_T)comment_col) {
pos.col = comment_col;
}
} else {
pos.col--;
@@ -2049,7 +2060,7 @@ pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel)
pos.col = 0;
do_quotes = -1;
line_breakcheck();
if (lisp) { // find comment pos in new line
if (lisp || skip_comments) { // find comment pos in new line
comment_col = check_linecomment(linep);
}
} else {
@@ -2057,6 +2068,35 @@ pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel)
}
}
// Track block comment state when FM_SKIPCOMM is set.
// Backward: '/' of end-marker enters comment; '*' of start-marker exits.
// Forward: '/' of start-marker enters comment; '/' of end-marker exits.
if (skip_comments && !comment_dir) {
if (backwards) {
// Guard pos.col < comment_col: don't misread '* /' at the '//'
// position as a block-comment end-marker.
if (!in_block_comment && pos.col > 0
&& linep[pos.col - 1] == '*' && linep[pos.col] == '/'
&& (comment_col == MAXCOL || (int)pos.col < comment_col)) {
in_block_comment = true;
} else if (in_block_comment && pos.col > 0
&& linep[pos.col - 1] == '/' && linep[pos.col] == '*') {
in_block_comment = false;
}
} else {
// Guard pos.col < comment_col: don't treat '/ *' inside a '//'
// comment as a block-comment start-marker.
if (!in_block_comment && linep[pos.col] == '/'
&& linep[pos.col + 1] == '*'
&& (comment_col == MAXCOL || (int)pos.col < comment_col)) {
in_block_comment = true;
} else if (in_block_comment && pos.col > 0
&& linep[pos.col - 1] == '*' && linep[pos.col] == '/') {
in_block_comment = false;
}
}
}
// If FM_BLOCKSTOP given, stop at a '{' or '}' in column 0.
if (pos.col == 0 && (flags & FM_BLOCKSTOP)
&& (linep[0] == '{' || linep[0] == '}')) {
@@ -2258,6 +2298,13 @@ pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel)
break;
}
// Skip matches inside comments when FM_SKIPCOMM is set.
if (skip_comments
&& (in_block_comment
|| (comment_col != MAXCOL && (int)pos.col >= comment_col))) {
break;
}
// Check for match outside of quotes, and inside of
// quotes when the start is also inside of quotes.
if ((!inquote || start_in_quotes == kTrue)
+1 -1
View File
@@ -49,7 +49,7 @@ enum {
FM_BACKWARD = 0x01, ///< search backwards
FM_FORWARD = 0x02, ///< search forwards
FM_BLOCKSTOP = 0x04, ///< stop at start/end of block
FM_SKIPCOMM = 0x08, ///< skip comments
FM_SKIPCOMM = 0x08, ///< skip comments (cursor must start outside)
};
/// Values for sub_cmd and which_pat argument for search_regcomp()
+80
View File
@@ -5512,5 +5512,85 @@ func Test_find_brace_backwards()
bwipe!
endfunc
" Brackets inside comments must not affect C indent calculation (FM_SKIPCOMM)
func Test_cindent_comment_brackets()
" stray } in inline block comment must not confuse enclosing-brace search
new
setl cindent sw=4
let code =<< trim [CODE]
int foo() {
/* } */
int bar;
}
[CODE]
call setline(1, code)
call cursor(3, 1)
normal ==
call assert_equal(' int bar;', getline(3))
bwipe!
" stray } in // line comment: same
new
setl cindent sw=4
let code2 =<< trim [CODE]
int foo() {
// }
int bar;
}
[CODE]
call setline(1, code2)
call cursor(3, 1)
normal ==
call assert_equal(' int bar;', getline(3))
bwipe!
" stray } on continuation line inside multi-line block comment
new
setl cindent sw=4
let code3 =<< trim [CODE]
int foo() {
/*
}
*/
int bar;
}
[CODE]
call setline(1, code3)
call cursor(5, 1)
normal ==
call assert_equal(' int bar;', getline(5))
bwipe!
" { in inline block comment must not be treated as enclosing brace
new
setl cindent sw=4
let code4 =<< trim [CODE]
int foo() {
/* { */
int bar;
}
[CODE]
call setline(1, code4)
call cursor(3, 1)
normal ==
call assert_equal(' int bar;', getline(3))
bwipe!
" ) in inline block comment must not be treated as enclosing brace
new
setl cindent sw=4
let code5 =<< trim [CODE]
some_func(arg1,
/* ) */ arg2,
arg3);
[CODE]
call setline(1, code5)
call cursor(3, 1)
normal ==
call assert_equal(' arg3);', getline(3))
bwipe!
endfunc
" vim: shiftwidth=2 sts=2 expandtab