Hi,
The goto declaration commands (gd and gD) only discards completely
commented out lines. In the following scenario pressing 'gd' would place
the cursor on the occurrence of x inside the trailing comment:
int func(int x) /* x is an int */
{
return x;
^
|
cursor
}
This is by design, the search is not terminated at the first occurrence
of the identifier in order to handle K&R function declarations
correctly.
The attached patch add support for discarding any type of C-style
comment. Thus, it does not respect the commentstring option. I think
this is reasonable since the behavior of gd and gD is tailored for C
code but occasionally works well with other similar languages. Another
limitation of the attached patch is that it does not recognize nested
multi-line comments (/* /* nested */ */) but should one care?
While at it, improve the test coverage for both gd and gD.
--
--
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].
For more options, visit https://groups.google.com/d/optout.
diff --git a/src/normal.c b/src/normal.c
index bbcd618..ce75e90 100644
--- a/src/normal.c
+++ b/src/normal.c
@@ -4238,6 +4238,46 @@ nv_gd(
}
/*
+ * Find the first inline C-style comment in line starting from offset. The
+ * comment boundaries (inclusive) will be written to start and end. Return
TRUE
+ * if a comment is found, FALSE otherwise.
+ */
+ static int
+find_comment(char_u *line, int offset, int *start, int *end)
+{
+ int i;
+ int incomment = FALSE;
+ int prev = 0;
+
+ for (i = offset; line[i] != NUL; i++)
+ {
+ if (incomment)
+ {
+ if (prev == '*' && line[i] == '/')
+ {
+ *end = i;
+ return TRUE;
+ }
+ }
+ else if (prev == '/' && line[i] == '*')
+ {
+ incomment = TRUE;
+ *start = i > 0 ? i - 1 : 0;
+ }
+ else if (prev == '/' && line[i] == '/')
+ {
+ *start = i > 0 ? i - 1 : 0;
+ *end = *start + STRLEN(&line[i]);
+ return TRUE;
+ }
+
+ prev = line[i];
+ }
+
+ return FALSE;
+}
+
+/*
* Search for variable declaration of "ptr[len]".
* When "locally" is TRUE in the current function ("gd"), otherwise in the
* current file ("gD").
@@ -4262,6 +4302,9 @@ find_decl(
int retval = OK;
int incll;
int searchflags = flags_arg;
+ int cstart; /* comment start offset */
+ int cend; /* comment end offset */
+ int incomment;
if ((pat = alloc(len + 7)) == NULL)
return FAIL;
@@ -4299,6 +4342,7 @@ find_decl(
clearpos(&found_pos);
for (;;)
{
+ incomment = 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)
@@ -4335,9 +4379,33 @@ find_decl(
continue;
}
#endif
- if (!locally) /* global search: use first match found */
+
+ /* If the identifier is present inside a comment and a previous match is
+ * present, favor that one instead. */
+ for (cstart = 0;
+ find_comment(ml_get_curline(), cstart, &cstart, &cend) == TRUE;
+ cstart++)
+ {
+ if (curwin->w_cursor.col > cstart
+ && curwin->w_cursor.col + len < cend)
+ {
+ if (found_pos.lnum != 0)
+ {
+ curwin->w_cursor = found_pos;
+ goto done;
+ }
+ else
+ {
+ incomment = TRUE;
+ break;
+ }
+ }
+ }
+
+ /* Global search: use first match found unless inside comment */
+ if (!incomment && !locally)
break;
- if (curwin->w_cursor.lnum >= par_pos.lnum)
+ if (!incomment && curwin->w_cursor.lnum >= par_pos.lnum)
{
/* If we previously found a valid position, use it. */
if (found_pos.lnum != 0)
@@ -4345,14 +4413,24 @@ 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 (incomment)
+ {
+ /* 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;
}
+done:
if (t == FAIL)
{
retval = FAIL;
diff --git a/src/testdir/test_goto.vim b/src/testdir/test_goto.vim
index 2afd96b..e1b90fa 100644
--- a/src/testdir/test_goto.vim
+++ b/src/testdir/test_goto.vim
@@ -1,20 +1,176 @@
" 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 key. The cursor should now be
positioned
+" at the given line and col.
+func XTest_goto_decl(key, 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:key
+ call assert_equal(a:line, line('.'))
+ call assert_equal(a:col, col('.'))
quit!
endfunc
+func Test_geeDEE()
+ let lines = [
+ \ 'int x;',
+ \ '',
+ \ 'int func(void)',
+ \ '{',
+ \ ' return x;',
+ \ '}',
+ \ ]
+ call XTest_goto_decl('gD', lines, 1, 5)
+endfunc
+
+func Test_geeDEE_comment()
+ let lines = [
+ \ '/* int x; */',
+ \ '',
+ \ 'int x;',
+ \ '',
+ \ 'int func(void)',
+ \ '{',
+ \ ' return x;',
+ \ '}',
+ \ ]
+ call XTest_goto_decl('gD', lines, 3, 5)
+endfunc
+
+func Test_geeDEE_inline_comment()
+ let lines = [
+ \ 'int y /* , x */;',
+ \ '',
+ \ 'int x;',
+ \ '',
+ \ 'int func(void)',
+ \ '{',
+ \ ' return x;',
+ \ '}',
+ \ ]
+ call XTest_goto_decl('gD', lines, 3, 5)
+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!
+ let lines = [
+ \ 'int x;',
+ \ '',
+ \ 'int func(int x)',
+ \ '{',
+ \ ' return x;',
+ \ '}',
+ \ ]
+ call XTest_goto_decl('gd', lines, 3, 14)
+endfunc
+
+func Test_gee_dee_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_gee_dee_kr_style()
+ let lines = [
+ \ 'int func(x)',
+ \ ' int x;',
+ \ '{',
+ \ ' return x;',
+ \ '}',
+ \ ]
+ call XTest_goto_decl('gd', lines, 2, 7)
+endfunc
+
+func Test_gee_dee_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_gee_dee_comment()
+ let lines = [
+ \ 'int func(void)',
+ \ '{',
+ \ ' /* int x; */',
+ \ ' int x;',
+ \ ' return x;',
+ \ '}',
+ \]
+ call XTest_goto_decl('gd', lines, 4, 7)
+endfunc
+
+func Test_gee_dee_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_gee_dee_inline_comment_only()
+ let lines = [
+ \ 'int func(void) /* one lonely x */',
+ \ '{',
+ \ ' return x;',
+ \ '}',
+ \ ]
+ call XTest_goto_decl('gd', lines, 3, 10)
+endfunc
+
+func Test_gee_dee_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_gee_dee_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_gee_dee_trailing_comment()
+ let lines = [
+ \ 'int func(int x) // x is an int',
+ \ '{',
+ \ ' return x;',
+ \ '}',
+ \ ]
+ call XTest_goto_decl('gd', lines, 1, 14)
endfunc