patch 9.2.0640: the "%" command jumps to parens and braces inside comments
Commit: https://github.com/vim/vim/commit/b8a109dcfb96f213328a966a7e38eb28da697229 Author: Hirohito Higashi <[email protected]> Date: Sat Jun 13 19:39:54 2026 +0000 patch 9.2.0640: the "%" command jumps to parens and braces inside comments Problem: The "%" command jumps to parens and braces inside comments, unlike the "=" operator (cindent), which ignores them. Solution: When 'comments' defines C-style comments and "%" is not in 'cpoptions', skip matching parens inside such comments, except when the cursor is inside a comment so a match there can still be found. fixes: #20329 related: #20111 closes: #20491 Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> Signed-off-by: Hirohito Higashi <[email protected]> Signed-off-by: Christian Brabandt <[email protected]> diff --git a/runtime/doc/motion.txt b/runtime/doc/motion.txt index 600ce647c..a78ee204e 100644 --- a/runtime/doc/motion.txt +++ b/runtime/doc/motion.txt @@ -1,4 +1,4 @@ -*motion.txt* For Vim version 9.2. Last change: 2026 Feb 14 +*motion.txt* For Vim version 9.2. Last change: 2026 Jun 13 VIM REFERENCE MANUAL by Bram Moolenaar @@ -1294,9 +1294,13 @@ remembered. quotes). Note that this works fine for C, but not for Perl, where single quotes are used for strings. - Nothing special is done for matches in comments. You - can either use the matchit plugin |matchit-install| or - put quotes around matches. + When in addition 'comments' defines C-style "//" or + "/*" comments, parens and braces inside such comments + are skipped, like the |=| operator does. This does + not happen when the cursor is inside a comment, so a + match inside that comment can still be found. + Otherwise you can use the matchit plugin + |matchit-install| or put quotes around matches. No count is allowed, {count}% jumps to a line {count} percentage down the file |N%|. Using '%' on diff --git a/runtime/doc/version9.txt b/runtime/doc/version9.txt index c7fb72cdd..a7778b5fe 100644 --- a/runtime/doc/version9.txt +++ b/runtime/doc/version9.txt @@ -52649,6 +52649,8 @@ Other ~ - Channel can handle |Blob| messages |channel-open-options|. - Added the "u" flag to 'shortmess' to silence undo/redo messages: |shm-u| - |C-indenting| detects comments better. +- The |%| command skips parens inside comments when 'comments' defines + C-style "//" or "/*" comments. - The |package-hlyank| can now optionally highlight the last put region as well. - Support %0{} in 'statusline' to insert the expression result verbatim and diff --git a/src/normal.c b/src/normal.c index b74c937c7..b803b88f6 100644 --- a/src/normal.c +++ b/src/normal.c @@ -4620,6 +4620,30 @@ nv_brackets(cmdarg_T *cap) clearopbeep(cap->oap); } +/* + * Return true when 'comments' defines a C-style line ("//") or block comment. + * This is when "%" should skip matching parens in comments, like the "=" + * operator does. + */ + static bool +buf_has_cstyle_comments(void) +{ + char_u *list; + char_u part_buf[COM_MAX_LEN]; // buffer for one 'comments' part + + for (list = curbuf->b_p_com; *list; ) + { + char_u *string; + + (void)copy_option_part(&list, part_buf, COM_MAX_LEN, ","); + string = vim_strchr(part_buf, ':'); // flags and comment leader + if (string != NULL && string[1] == '/' + && (string[2] == '/' || string[2] == '*')) + return true; + } + return false; +} + /* * Handle Normal mode "%" command. */ @@ -4659,9 +4683,23 @@ nv_percent(cmdarg_T *cap) } else // "%" : go to matching paren { + int flags = 0; + + // Skip matching parens inside C-style comments, like the "=" operator + // does, but not when "%" is in 'cpoptions' (Vi-compatible) or the + // cursor sits in a line comment (so a match there can still be found). + if (vim_strchr(p_cpo, CPO_MATCH) == NULL && buf_has_cstyle_comments()) + { + int comment_col = check_linecomment(ml_get_curline()); + + if (comment_col == MAXCOL + || curwin->w_cursor.col < (colnr_T)comment_col) + flags = FM_SKIPCOMM; + } + cap->oap->motion_type = MCHAR; cap->oap->use_reg_one = TRUE; - if ((pos = findmatch(cap->oap, NUL)) == NULL) + if ((pos = findmatchlimit(cap->oap, NUL, flags, 0)) == NULL) clearopbeep(cap->oap); else { diff --git a/src/testdir/test_normal.vim b/src/testdir/test_normal.vim index eea789123..b4298e304 100644 --- a/src/testdir/test_normal.vim +++ b/src/testdir/test_normal.vim @@ -3896,6 +3896,70 @@ func Test_normal_percent_jump() bwipe! endfunc +" Test that "%" skips parens inside comments when 'comments' defines C-style +" "//" or "/*" comments. +func Test_normal_percent_skip_comment() + new + setlocal comments=s1:/*,mb:*,ex:*/,:// + + " Forward: skip a ")" inside a // comment, match the real one. + silent! %delete _ + call setline(1, ['foo( // )', ');']) + call cursor(1, 4) + normal % + call assert_equal([2, 1], [line('.'), col('.')]) + + " Forward: skip a ")" inside a /* */ comment, match the real one. + silent! %delete _ + call setline(1, ['bar( /* ) */ x)']) + call cursor(1, 4) + normal % + call assert_equal([1, 15], [line('.'), col('.')]) + + " Backward: skip a "(" inside a // comment, match the real one. + silent! %delete _ + call setline(1, ['( // (', ')']) + call cursor(2, 1) + normal % + call assert_equal([1, 1], [line('.'), col('.')]) + + " Cursor inside a // comment: a match inside that comment is still found. + silent! %delete _ + call setline(1, ['x // ( y )']) + call cursor(1, 6) + normal % + call assert_equal([1, 10], [line('.'), col('.')]) + + " Cursor inside a /* */ comment: a match inside that comment is still found. + silent! %delete _ + call setline(1, ['/* a ( b ) c */']) + call cursor(1, 6) + normal % + call assert_equal([1, 10], [line('.'), col('.')]) + + " When 'comments' has no C-style comments the parens are not skipped. + setlocal comments=b:# + silent! %delete _ + call setline(1, ['foo( // )', ');']) + call cursor(1, 4) + normal % + call assert_equal([1, 10], [line('.'), col('.')]) + + " With "%" in 'cpoptions' Vi-compatible matching is used and the parens + " inside comments are not skipped. + let save_cpo = &cpoptions + setlocal comments=s1:/*,mb:*,ex:*/,:// + set cpoptions+=% + silent! %delete _ + call setline(1, ['foo( // )', ');']) + call cursor(1, 4) + normal % + call assert_equal([1, 10], [line('.'), col('.')]) + let &cpoptions = save_cpo + + bwipe! +endfunc + " Test for << and >> commands to shift text by 'shiftwidth' func Test_normal_shift_rightleft() new diff --git a/src/version.c b/src/version.c index 1fdf8d1b0..2e42bb357 100644 --- a/src/version.c +++ b/src/version.c @@ -759,6 +759,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 640, /**/ 639, /**/ -- -- You received this message from the "vim_dev" maillist. Do not top-post! Type your reply below the text you are replying to. For more information, visit http://www.vim.org/maillist.php --- You received this message because you are subscribed to the Google Groups "vim_dev" group. To unsubscribe from this group and stop receiving emails from it, send an email to [email protected]. To view this discussion visit https://groups.google.com/d/msgid/vim_dev/E1wYUWS-00DW2v-8d%40256bit.org.
