From 226630a030c0d41145e1109f09633360fc9c999d Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sat, 8 Oct 2016 19:21:31 +0200 Subject: patch 8.0.0023 Problem: "gd" and "gD" may find a match in a comment or string. Solution: Ignore matches in comments and strings. (Anton Lindqvist) --- src/normal.c | 82 ++++++++++++-- src/testdir/test_goto.vim | 281 +++++++++++++++++++++++++++++++++++++++++++--- src/version.c | 2 + 3 files changed, 345 insertions(+), 20 deletions(-) (limited to 'src') diff --git a/src/normal.c b/src/normal.c index 8302ffbc9..99ced410d 100644 --- a/src/normal.c +++ b/src/normal.c @@ -4239,6 +4239,52 @@ nv_gd( #endif } +/* + * Return TRUE if line[offset] is not inside a C-style comment or string, FALSE + * otherwise. + */ + static int +is_ident(char_u *line, int offset) +{ + int i; + int incomment = FALSE; + int instring = 0; + int prev = 0; + + for (i = 0; i < offset && line[i] != NUL; i++) + { + if (instring != 0) + { + if (prev != '\\' && line[i] == instring) + instring = 0; + } + else if ((line[i] == '"' || line[i] == '\'') && !incomment) + { + instring = line[i]; + } + else + { + if (incomment) + { + if (prev == '*' && line[i] == '/') + incomment = FALSE; + } + else if (prev == '/' && line[i] == '*') + { + incomment = TRUE; + } + else if (prev == '/' && line[i] == '/') + { + return FALSE; + } + } + + prev = line[i]; + } + + return incomment == FALSE && instring == 0; +} + /* * Search for variable declaration of "ptr[len]". * When "locally" is TRUE in the current function ("gd"), otherwise in the @@ -4264,6 +4310,7 @@ find_decl( int retval = OK; int incll; int searchflags = flags_arg; + int valid; if ((pat = alloc(len + 7)) == NULL) return FAIL; @@ -4301,6 +4348,7 @@ find_decl( clearpos(&found_pos); for (;;) { + valid = FALSE; t = searchit(curwin, curbuf, &curwin->w_cursor, FORWARD, pat, 1L, searchflags, RE_LAST, (linenr_T)0, NULL); if (curwin->w_cursor.lnum >= old_pos.lnum) @@ -4337,9 +4385,20 @@ find_decl( continue; } #endif - if (!locally) /* global search: use first match found */ + valid = is_ident(ml_get_curline(), curwin->w_cursor.col); + + /* If the current position is not a valid identifier and a previous + * match is present, favor that one instead. */ + if (!valid && found_pos.lnum != 0) + { + curwin->w_cursor = found_pos; break; - if (curwin->w_cursor.lnum >= par_pos.lnum) + } + + /* Global search: use first valid match found */ + if (valid && !locally) + break; + if (valid && curwin->w_cursor.lnum >= par_pos.lnum) { /* If we previously found a valid position, use it. */ if (found_pos.lnum != 0) @@ -4347,11 +4406,20 @@ find_decl( break; } - /* For finding a local variable and the match is before the "{" search - * to find a later match. For K&R style function declarations this - * skips the function header without types. Remove SEARCH_START from - * flags to avoid getting stuck at one position. */ - found_pos = curwin->w_cursor; + /* For finding a local variable and the match is before the "{" or + * inside a comment, continue searching. For K&R style function + * declarations this skips the function header without types. */ + if (!valid) + { + /* Braces needed due to macro expansion of clearpos. */ + clearpos(&found_pos); + } + else + { + found_pos = curwin->w_cursor; + } + /* Remove SEARCH_START from flags to avoid getting stuck at one + * position. */ searchflags &= ~SEARCH_START; } diff --git a/src/testdir/test_goto.vim b/src/testdir/test_goto.vim index 2afd96b29..16daaf862 100644 --- a/src/testdir/test_goto.vim +++ b/src/testdir/test_goto.vim @@ -1,20 +1,275 @@ " Test commands that jump somewhere. -func Test_geeDEE() +" Create a new buffer using "lines" and place the cursor on the word after the +" first occurrence of return and invoke "cmd". The cursor should now be +" positioned at the given line and col. +func XTest_goto_decl(cmd, lines, line, col) new - call setline(1, ["Filename x;", "", "int Filename", "int func() {", "Filename y;"]) - /y;/ - normal gD - call assert_equal(1, line('.')) + call setline(1, a:lines) + /return/ + normal! W + execute 'norm! ' . a:cmd + call assert_equal(a:line, line('.')) + call assert_equal(a:col, col('.')) quit! endfunc -func Test_gee_dee() - new - call setline(1, ["int x;", "", "int func(int x)", "{", " return x;", "}"]) - /return/ - normal $hgd - call assert_equal(3, line('.')) - call assert_equal(14, col('.')) - quit! +func Test_gD() + let lines = [ + \ 'int x;', + \ '', + \ 'int func(void)', + \ '{', + \ ' return x;', + \ '}', + \ ] + call XTest_goto_decl('gD', lines, 1, 5) +endfunc + +func Test_gD_too() + let lines = [ + \ 'Filename x;', + \ '', + \ 'int Filename', + \ 'int func() {', + \ ' Filename x;', + \ ' return x;', + \ ] + call XTest_goto_decl('gD', lines, 1, 10) +endfunc + +func Test_gD_comment() + let lines = [ + \ '/* int x; */', + \ 'int x;', + \ '', + \ 'int func(void)', + \ '{', + \ ' return x;', + \ '}', + \ ] + call XTest_goto_decl('gD', lines, 2, 5) +endfunc + +func Test_gD_inline_comment() + let lines = [ + \ 'int y /* , x */;', + \ 'int x;', + \ '', + \ 'int func(void)', + \ '{', + \ ' return x;', + \ '}', + \ ] + call XTest_goto_decl('gD', lines, 2, 5) +endfunc + +func Test_gD_string() + let lines = [ + \ 'char *s[] = "x";', + \ 'int x = 1;', + \ '', + \ 'int func(void)', + \ '{', + \ ' return x;', + \ '}', + \ ] + call XTest_goto_decl('gD', lines, 2, 5) +endfunc + +func Test_gD_string_same_line() + let lines = [ + \ 'char *s[] = "x", int x = 1;', + \ '', + \ 'int func(void)', + \ '{', + \ ' return x;', + \ '}', + \ ] + call XTest_goto_decl('gD', lines, 1, 22) +endfunc + +func Test_gD_char() + let lines = [ + \ "char c = 'x';", + \ 'int x = 1;', + \ '', + \ 'int func(void)', + \ '{', + \ ' return x;', + \ '}', + \ ] + call XTest_goto_decl('gD', lines, 2, 5) +endfunc + +func Test_gd() + let lines = [ + \ 'int x;', + \ '', + \ 'int func(int x)', + \ '{', + \ ' return x;', + \ '}', + \ ] + call XTest_goto_decl('gd', lines, 3, 14) +endfunc + +func Test_gd_not_local() + let lines = [ + \ 'int func1(void)', + \ '{', + \ ' return x;', + \ '}', + \ '', + \ 'int func2(int x)', + \ '{', + \ ' return x;', + \ '}', + \ ] + call XTest_goto_decl('gd', lines, 3, 10) +endfunc + +func Test_gd_kr_style() + let lines = [ + \ 'int func(x)', + \ ' int x;', + \ '{', + \ ' return x;', + \ '}', + \ ] + call XTest_goto_decl('gd', lines, 2, 7) +endfunc + +func Test_gd_missing_braces() + let lines = [ + \ 'def func1(a)', + \ ' a + 1', + \ 'end', + \ '', + \ 'a = 1', + \ '', + \ 'def func2()', + \ ' return a', + \ 'end', + \ ] + call XTest_goto_decl('gd', lines, 1, 11) +endfunc + +func Test_gd_comment() + let lines = [ + \ 'int func(void)', + \ '{', + \ ' /* int x; */', + \ ' int x;', + \ ' return x;', + \ '}', + \] + call XTest_goto_decl('gd', lines, 4, 7) +endfunc + +func Test_gd_comment_in_string() + let lines = [ + \ 'int func(void)', + \ '{', + \ ' char *s ="//"; int x;', + \ ' int x;', + \ ' return x;', + \ '}', + \] + call XTest_goto_decl('gd', lines, 3, 22) +endfunc + +func Test_gd_string_in_comment() + set comments= + let lines = [ + \ 'int func(void)', + \ '{', + \ ' /* " */ int x;', + \ ' int x;', + \ ' return x;', + \ '}', + \] + call XTest_goto_decl('gd', lines, 3, 15) + set comments& +endfunc + +func Test_gd_inline_comment() + let lines = [ + \ 'int func(/* x is an int */ int x)', + \ '{', + \ ' return x;', + \ '}', + \ ] + call XTest_goto_decl('gd', lines, 1, 32) +endfunc + +func Test_gd_inline_comment_only() + let lines = [ + \ 'int func(void) /* one lonely x */', + \ '{', + \ ' return x;', + \ '}', + \ ] + call XTest_goto_decl('gd', lines, 3, 10) +endfunc + +func Test_gd_inline_comment_body() + let lines = [ + \ 'int func(void)', + \ '{', + \ ' int y /* , x */;', + \ '', + \ ' for (/* int x = 0 */; y < 2; y++);', + \ '', + \ ' int x = 0;', + \ '', + \ ' return x;', + \ '}', + \ ] + call XTest_goto_decl('gd', lines, 7, 7) +endfunc + +func Test_gd_trailing_multiline_comment() + let lines = [ + \ 'int func(int x) /* x is an int */', + \ '{', + \ ' return x;', + \ '}', + \ ] + call XTest_goto_decl('gd', lines, 1, 14) +endfunc + +func Test_gd_trailing_comment() + let lines = [ + \ 'int func(int x) // x is an int', + \ '{', + \ ' return x;', + \ '}', + \ ] + call XTest_goto_decl('gd', lines, 1, 14) +endfunc + +func Test_gd_string() + let lines = [ + \ 'int func(void)', + \ '{', + \ ' char *s = "x";', + \ ' int x = 1;', + \ '', + \ ' return x;', + \ '}', + \ ] + call XTest_goto_decl('gd', lines, 4, 7) +endfunc + +func Test_gd_string_only() + let lines = [ + \ 'int func(void)', + \ '{', + \ ' char *s = "x";', + \ '', + \ ' return x;', + \ '}', + \ ] + call XTest_goto_decl('gd', lines, 5, 10) endfunc diff --git a/src/version.c b/src/version.c index 4fd2982a5..5bb9a1492 100644 --- a/src/version.c +++ b/src/version.c @@ -764,6 +764,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 23, /**/ 22, /**/ -- cgit v1.2.3