]> granicus.if.org Git - vim/commitdiff
patch 8.2.0088: insufficient tests for tags; bug in using extra tag field v8.2.0088
authorBram Moolenaar <Bram@vim.org>
Sun, 5 Jan 2020 19:35:44 +0000 (20:35 +0100)
committerBram Moolenaar <Bram@vim.org>
Sun, 5 Jan 2020 19:35:44 +0000 (20:35 +0100)
Problem:    Insufficient tests for tags; bug in using extra tag field when
            using an ex command to position the cursor.
Solution:   Fix the bug, add more tests. (Yegappan Lakshmanan, closes #5439)

runtime/doc/tagsrch.txt
src/tag.c
src/testdir/test_ins_complete.vim
src/testdir/test_tagfunc.vim
src/testdir/test_tagjump.vim
src/testdir/test_taglist.vim
src/version.c

index 1f8eed4f875c03b3a2af33c4ecf7e09cb3ad97d7..f57b9c5297ac1ac8acb87dcd1af5faf57f0fe22d 100644 (file)
@@ -344,11 +344,11 @@ the same as above, with a "p" prepended.
 A static tag is a tag that is defined for a specific file.  In a C program
 this could be a static function.
 
-In Vi jumping to a tag sets the current search pattern.  This means that
-the "n" command after jumping to a tag does not search for the same pattern
-that it did before jumping to the tag.  Vim does not do this as we consider it
-to be a bug.  You can still find the tag search pattern in the search history.
-If you really want the old Vi behavior, set the 't' flag in 'cpoptions'.
+In Vi jumping to a tag sets the current search pattern.  This means that the
+"n" command after jumping to a tag does not search for the same pattern that
+it did before jumping to the tag.  Vim does not do this as we consider it to
+be a bug.  If you really want the old Vi behavior, set the 't' flag in
+'cpoptions'.
 
                                                        *tag-binary-search*
 Vim uses binary searching in the tags file to find the desired tag quickly
@@ -426,8 +426,7 @@ would otherwise go unnoticed.  Example: >
 
 In Vi the ":tag" command sets the last search pattern when the tag is searched
 for.  In Vim this is not done, the previous search pattern is still remembered,
-unless the 't' flag is present in 'cpoptions'.  The search pattern is always
-put in the search history, so you can modify it if searching fails.
+unless the 't' flag is present in 'cpoptions'.
 
                                        *emacs-tags* *emacs_tags* *E430*
 Emacs style tag files are only supported if Vim was compiled with the
index 4f897fa607234e8313cff0df2527b31ae47a2787..aaf19c97386bdaf07319e0f53653df408563be6e 100644 (file)
--- a/src/tag.c
+++ b/src/tag.c
@@ -3808,6 +3808,7 @@ test_for_current(
 find_extra(char_u **pp)
 {
     char_u     *str = *pp;
+    char_u     first_char = **pp;
 
     // Repeat for addresses separated with ';'
     for (;;)
@@ -3817,7 +3818,7 @@ find_extra(char_u **pp)
        else if (*str == '/' || *str == '?')
        {
            str = skip_regexp(str + 1, *str, FALSE, NULL);
-           if (*str != **pp)
+           if (*str != first_char)
                str = NULL;
            else
                ++str;
@@ -3837,6 +3838,7 @@ find_extra(char_u **pp)
                  || !(VIM_ISDIGIT(str[1]) || str[1] == '/' || str[1] == '?'))
            break;
        ++str;  // skip ';'
+       first_char = *str;
     }
 
     if (str != NULL && STRNCMP(str, ";\"", 2) == 0)
index bec6ffc6f91c1b35d2bb4cfd35bc4036ef2a7245..c9d93ceb1e263496ad3138219432ee9e15b72d42 100644 (file)
@@ -432,3 +432,31 @@ func Test_pum_with_preview_win()
   call StopVimInTerminal(buf)
   call delete('Xpreviewscript')
 endfunc
+
+" Test for inserting the tag search pattern in insert mode
+func Test_ins_compl_tag_sft()
+  call writefile([
+        \ "!_TAG_FILE_ENCODING\tutf-8\t//",
+        \ "first\tXfoo\t/^int first() {}$/",
+        \ "second\tXfoo\t/^int second() {}$/",
+        \ "third\tXfoo\t/^int third() {}$/"],
+        \ 'Xtags')
+  set tags=Xtags
+  let code =<< trim [CODE]
+    int first() {}
+    int second() {}
+    int third() {}
+  [CODE]
+  call writefile(code, 'Xfoo')
+
+  enew
+  set showfulltag
+  exe "normal isec\<C-X>\<C-]>\<C-N>\<CR>"
+  call assert_equal('int second() {}', getline(1))
+  set noshowfulltag
+
+  call delete('Xtags')
+  call delete('Xfoo')
+  set tags&
+  %bwipe!
+endfunc
index 242aa3a23542808ed0dd1bfbbf8efbcbb64baa3c..97782ae21def3f20f1c755ee7f3d49d940b16d6f 100644 (file)
@@ -81,4 +81,28 @@ func Test_tagfunc()
   call delete('Xfile1')
 endfunc
 
+" Test for modifying the tag stack from a tag function and jumping to a tag
+" from a tag function
+func Test_tagfunc_settagstack()
+  func Mytagfunc1(pat, flags, info)
+    call settagstack(1, {'tagname' : 'mytag', 'from' : [0, 10, 1, 0]})
+    return [{'name' : 'mytag', 'filename' : 'Xtest', 'cmd' : '1'}]
+  endfunc
+  set tagfunc=Mytagfunc1
+  call writefile([''], 'Xtest')
+  call assert_fails('tag xyz', 'E986:')
+
+  func Mytagfunc2(pat, flags, info)
+    tag test_tag
+    return [{'name' : 'mytag', 'filename' : 'Xtest', 'cmd' : '1'}]
+  endfunc
+  set tagfunc=Mytagfunc2
+  call assert_fails('tag xyz', 'E986:')
+
+  call delete('Xtest')
+  set tagfunc&
+  delfunc Mytagfunc1
+  delfunc Mytagfunc2
+endfunc
+
 " vim: shiftwidth=2 sts=2 expandtab
index 8d32883da207541dd224a4af45ce992b78c7c5a8..d7bc2992eebe1861527d44b9da1b607c01f51f1d 100644 (file)
@@ -578,4 +578,293 @@ func Test_tagline()
   set tags&
 endfunc
 
+" Test for expanding environment variable in a tag file name
+func Test_tag_envvar()
+  call writefile(["Func1\t$FOO\t/^Func1/"], 'Xtags')
+  set tags=Xtags
+
+  let $FOO='TagTestEnv'
+
+  let caught_exception = v:false
+  try
+    tag Func1
+  catch /E429:/
+    call assert_match('E429:.*"TagTestEnv".*', v:exception)
+    let caught_exception = v:true
+  endtry
+  call assert_true(caught_exception)
+
+  set tags&
+  call delete('Xtags')
+  unlet $FOO
+endfunc
+
+" Test for :ptag
+func Test_ptag()
+  call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//",
+        \ "second\tXfile1\t2",
+        \ "third\tXfile1\t3",],
+        \ 'Xtags')
+  set tags=Xtags
+  call writefile(['first', 'second', 'third'], 'Xfile1')
+
+  enew | only
+  ptag third
+  call assert_equal(2, winnr())
+  call assert_equal(2, winnr('$'))
+  call assert_equal(1, getwinvar(1, '&previewwindow'))
+  call assert_equal(0, getwinvar(2, '&previewwindow'))
+  wincmd w
+  call assert_equal(3, line('.'))
+
+  " jump to the tag again
+  ptag third
+  call assert_equal(3, line('.'))
+
+  " close the preview window
+  pclose
+  call assert_equal(1, winnr('$'))
+
+  call delete('Xfile1')
+  call delete('Xtags')
+  set tags&
+endfunc
+
+" Tests for guessing the tag location
+func Test_tag_guess()
+  call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//",
+        \ "func1\tXfoo\t/^int func1(int x)/",
+        \ "func2\tXfoo\t/^int func2(int y)/",
+        \ "func3\tXfoo\t/^func3/",
+        \ "func4\tXfoo\t/^func4/"],
+        \ 'Xtags')
+  set tags=Xtags
+  let code =<< trim [CODE]
+
+    int FUNC1  (int x) { }
+    int 
+    func2   (int y) { }
+    int * func3 () { }
+
+  [CODE]
+  call writefile(code, 'Xfoo')
+
+  let v:statusmsg = ''
+  ta func1
+  call assert_match('E435:', v:statusmsg)
+  call assert_equal(2, line('.'))
+  let v:statusmsg = ''
+  ta func2
+  call assert_match('E435:', v:statusmsg)
+  call assert_equal(4, line('.'))
+  let v:statusmsg = ''
+  ta func3
+  call assert_match('E435:', v:statusmsg)
+  call assert_equal(5, line('.'))
+  call assert_fails('ta func4', 'E434:')
+
+  call delete('Xtags')
+  call delete('Xfoo')
+  set tags&
+endfunc
+
+" Test for an unsorted tags file
+func Test_tag_sort()
+  call writefile([
+        \ "first\tXfoo\t1",
+        \ "ten\tXfoo\t3",
+        \ "six\tXfoo\t2"],
+        \ 'Xtags')
+  set tags=Xtags
+  let code =<< trim [CODE]
+    int first() {}
+    int six() {}
+    int ten() {}
+  [CODE]
+  call writefile(code, 'Xfoo')
+
+  call assert_fails('tag first', 'E432:')
+
+  call delete('Xtags')
+  call delete('Xfoo')
+  set tags&
+  %bwipe
+endfunc
+
+" Test for an unsorted tags file
+func Test_tag_fold()
+  call writefile([
+        \ "!_TAG_FILE_ENCODING\tutf-8\t//",
+        \ "!_TAG_FILE_SORTED\t2\t/0=unsorted, 1=sorted, 2=foldcase/",
+        \ "first\tXfoo\t1",
+        \ "second\tXfoo\t2",
+        \ "third\tXfoo\t3"],
+        \ 'Xtags')
+  set tags=Xtags
+  let code =<< trim [CODE]
+    int first() {}
+    int second() {}
+    int third() {}
+  [CODE]
+  call writefile(code, 'Xfoo')
+
+  enew
+  tag second
+  call assert_equal('Xfoo', bufname(''))
+  call assert_equal(2, line('.'))
+
+  call delete('Xtags')
+  call delete('Xfoo')
+  set tags&
+  %bwipe
+endfunc
+
+" Test for the :ltag command
+func Test_ltag()
+  call writefile([
+        \ "!_TAG_FILE_ENCODING\tutf-8\t//",
+        \ "first\tXfoo\t1",
+        \ "second\tXfoo\t/^int second() {}$/",
+        \ "third\tXfoo\t3"],
+        \ 'Xtags')
+  set tags=Xtags
+  let code =<< trim [CODE]
+    int first() {}
+    int second() {}
+    int third() {}
+  [CODE]
+  call writefile(code, 'Xfoo')
+
+  enew
+  call setloclist(0, [], 'f')
+  ltag third
+  call assert_equal('Xfoo', bufname(''))
+  call assert_equal(3, line('.'))
+  call assert_equal([{'lnum': 3, 'bufnr': bufnr('Xfoo'), 'col': 0,
+        \ 'pattern': '', 'valid': 1, 'vcol': 0, 'nr': 0, 'type': '',
+        \ 'module': '', 'text': 'third'}], getloclist(0))
+
+  ltag second
+  call assert_equal(2, line('.'))
+  call assert_equal([{'lnum': 0, 'bufnr': bufnr('Xfoo'), 'col': 0,
+        \ 'pattern': '^\Vint second() {}\$', 'valid': 1, 'vcol': 0, 'nr': 0,
+        \ 'type': '', 'module': '', 'text': 'second'}], getloclist(0))
+
+  call delete('Xtags')
+  call delete('Xfoo')
+  set tags&
+  %bwipe
+endfunc
+
+" Test for setting the last search pattern to the tag search pattern
+" when cpoptions has 't'
+func Test_tag_last_search_pat()
+  call writefile([
+        \ "!_TAG_FILE_ENCODING\tutf-8\t//",
+        \ "first\tXfoo\t/^int first() {}/",
+        \ "second\tXfoo\t/^int second() {}/",
+        \ "third\tXfoo\t/^int third() {}/"],
+        \ 'Xtags')
+  set tags=Xtags
+  let code =<< trim [CODE]
+    int first() {}
+    int second() {}
+    int third() {}
+  [CODE]
+  call writefile(code, 'Xfoo')
+
+  enew
+  let save_cpo = &cpo
+  set cpo+=t
+  let @/ = ''
+  tag second
+  call assert_equal('^int second() {}', @/)
+  let &cpo = save_cpo
+
+  call delete('Xtags')
+  call delete('Xfoo')
+  set tags&
+  %bwipe
+endfunc
+
+" Test for jumping to a tag when the tag stack is full
+func Test_tag_stack_full()
+  let l = []
+  for i in range(10, 31)
+    let l += ["var" .. i .. "\tXfoo\t/^int var" .. i .. ";$/"]
+  endfor
+  call writefile(l, 'Xtags')
+  set tags=Xtags
+
+  let l = []
+  for i in range(10, 31)
+    let l += ["int var" .. i .. ";"]
+  endfor
+  call writefile(l, 'Xfoo')
+
+  enew
+  for i in range(10, 30)
+    exe "tag var" .. i
+  endfor
+  let l = gettagstack()
+  call assert_equal(20, l.length)
+  call assert_equal('var11', l.items[0].tagname)
+  tag var31
+  let l = gettagstack()
+  call assert_equal('var12', l.items[0].tagname)
+  call assert_equal('var31', l.items[19].tagname)
+
+  " Jump from the top of the stack
+  call assert_fails('tag', 'E556:')
+
+  " Pop from an unsaved buffer
+  enew!
+  call append(1, "sample text")
+  call assert_fails('pop', 'E37:')
+  call assert_equal(21, gettagstack().curidx)
+  enew!
+
+  " Pop all the entries in the tag stack
+  call assert_fails('30pop', 'E555:')
+
+  " Pop the tag stack when it is empty
+  call settagstack(1, {'items' : []})
+  call assert_fails('pop', 'E73:')
+
+  call delete('Xtags')
+  call delete('Xfoo')
+  set tags&
+  %bwipe
+endfunc
+
+" Test for browsing multiple matching tags
+func Test_tag_multimatch()
+  call writefile([
+        \ "!_TAG_FILE_ENCODING\tutf-8\t//",
+        \ "first\tXfoo\t1",
+        \ "first\tXfoo\t2",
+        \ "first\tXfoo\t3"],
+        \ 'Xtags')
+  set tags=Xtags
+  let code =<< trim [CODE]
+    int first() {}
+    int first() {}
+    int first() {}
+  [CODE]
+  call writefile(code, 'Xfoo')
+
+  tag first
+  tlast
+  call assert_equal(3, line('.'))
+  call assert_fails('tnext', 'E428:')
+  tfirst
+  call assert_equal(1, line('.'))
+  call assert_fails('tprev', 'E425:')
+
+  call delete('Xtags')
+  call delete('Xfoo')
+  set tags&
+  %bwipe
+endfunc
+
 " vim: shiftwidth=2 sts=2 expandtab
index 824adc013bc5b46cb975faf56f2d33abbbb579ef..0f2615d52066752070e306fa02348fd776c99003 100644 (file)
@@ -115,3 +115,99 @@ func Test_tagsfile_without_trailing_newline()
   call delete('Xtags')
   set tags&
 endfunc
+
+" Test for ignoring comments in a tags file
+func Test_tagfile_ignore_comments()
+  call writefile([
+       \ "!_TAG_PROGRAM_NAME   /Test tags generator/",
+       \ "FBar\tXfoo\t2" .. ';"' .. "\textrafield\tf",
+       \ "!_TAG_FILE_FORMAT    2       /extended format/",
+       \ ], 'Xtags')
+  set tags=Xtags
+
+  let l = taglist('.*')
+  call assert_equal(1, len(l))
+  call assert_equal('FBar', l[0].name)
+
+  set tags&
+  call delete('Xtags')
+endfunc
+
+" Test for using an excmd in a tags file to position the cursor (instead of a
+" search pattern or a line number)
+func Test_tagfile_excmd()
+  call writefile([
+       \ "vFoo\tXfoo\tcall cursor(3, 4)" .. '|;"' .. "\tv",
+       \ ], 'Xtags')
+  set tags=Xtags
+
+  let l = taglist('.*')
+  call assert_equal([{
+             \ 'cmd' : 'call cursor(3, 4)',
+             \ 'static' : 0,
+             \ 'name' : 'vFoo',
+             \ 'kind' : 'v',
+             \ 'filename' : 'Xfoo'}], l)
+
+  set tags&
+  call delete('Xtags')
+endfunc
+
+" Test for duplicate fields in a tag in a tags file
+func Test_duplicate_field()
+  call writefile([
+       \ "vFoo\tXfoo\t4" .. ';"' .. "\ttypename:int\ttypename:int\tv",
+       \ ], 'Xtags')
+  set tags=Xtags
+
+  let l = taglist('.*')
+  call assert_equal([{
+             \ 'cmd' : '4',
+             \ 'static' : 0,
+             \ 'name' : 'vFoo',
+             \ 'kind' : 'v',
+             \ 'typename' : 'int',
+             \ 'filename' : 'Xfoo'}], l)
+
+  set tags&
+  call delete('Xtags')
+endfunc
+
+" Test for tag address with ;
+func Test_tag_addr_with_semicolon()
+  call writefile([
+             \ "Func1\tXfoo\t6;/^Func1/" .. ';"' .. "\tf"
+             \ ], 'Xtags')
+  set tags=Xtags
+
+  let l = taglist('.*')
+  call assert_equal([{
+             \ 'cmd' : '6;/^Func1/',
+             \ 'static' : 0,
+             \ 'name' : 'Func1',
+             \ 'kind' : 'f',
+             \ 'filename' : 'Xfoo'}], l)
+
+  set tags&
+  call delete('Xtags')
+endfunc
+
+" Test for format error in a tags file
+func Test_format_error()
+  call writefile(['vFoo-Xfoo-4'], 'Xtags')
+  set tags=Xtags
+
+  let caught_exception = v:false
+  try
+    let l = taglist('.*')
+  catch /E431:/
+    " test succeeded
+    let caught_exception = v:true
+  catch
+    call assert_report('Caught ' . v:exception . ' in ' . v:throwpoint)
+  endtry
+  call assert_true(caught_exception)
+
+  set tags&
+  call delete('Xtags')
+endfunc
index dfa31a77e660bceffac343e42d841e110c86c3a7..c34675f46c54329f1fe8ff9996a08c66f7548282 100644 (file)
@@ -742,6 +742,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    88,
 /**/
     87,
 /**/