mirror of
https://github.com/neovim/neovim.git
synced 2026-06-15 16:23:48 +00:00
vim-patch:9.2.0640: the "%" command jumps to parens and braces inside comments (#40229)
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: vim/vim#20329
related: vim/vim#20111
closes: vim/vim#20491
b8a109dcfb
Co-authored-by: Hirohito Higashi <h.east.727@gmail.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1338,9 +1338,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 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 or put
|
||||
quotes around matches.
|
||||
|
||||
No count is allowed, {count}% jumps to a line {count}
|
||||
percentage down the file |N%|. Using '%' on
|
||||
|
||||
@@ -4356,6 +4356,23 @@ static void nv_brackets(cmdarg_T *cap)
|
||||
}
|
||||
}
|
||||
|
||||
/// 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 part_buf[COM_MAX_LEN]; // buffer for one 'comments' part
|
||||
|
||||
for (char *list = curbuf->b_p_com; *list;) {
|
||||
(void)copy_option_part(&list, part_buf, COM_MAX_LEN, ",");
|
||||
char *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.
|
||||
static void nv_percent(cmdarg_T *cap)
|
||||
{
|
||||
@@ -4384,10 +4401,21 @@ static void nv_percent(cmdarg_T *cap)
|
||||
beginline(BL_SOL | BL_FIX);
|
||||
}
|
||||
} 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(get_cursor_line_ptr());
|
||||
if (comment_col == MAXCOL || curwin->w_cursor.col < (colnr_T)comment_col) {
|
||||
flags = FM_SKIPCOMM;
|
||||
}
|
||||
}
|
||||
|
||||
pos_T *pos;
|
||||
cap->oap->motion_type = kMTCharWise;
|
||||
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 {
|
||||
setpcmark();
|
||||
|
||||
@@ -3908,6 +3908,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
|
||||
|
||||
Reference in New Issue
Block a user