changeset 19055:8645b73b3645 v8.2.0088

patch 8.2.0088: insufficient tests for tags; bug in using extra tag field Commit: https://github.com/vim/vim/commit/830c1afc9d2cd5819a05c71d4e0b1f748a8c0519 Author: Bram Moolenaar <Bram@vim.org> Date: Sun Jan 5 20:35:44 2020 +0100 patch 8.2.0088: insufficient tests for tags; bug in using extra tag field 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)
author Bram Moolenaar <Bram@vim.org>
date Sun, 05 Jan 2020 20:45:05 +0100
parents ebee9e4dac40
children 369253646046
files 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
diffstat 7 files changed, 448 insertions(+), 8 deletions(-) [+]
line wrap: on
line diff
--- a/runtime/doc/tagsrch.txt
+++ b/runtime/doc/tagsrch.txt
@@ -344,11 +344,11 @@ 4. Tags details						*tag-details*
 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
--- 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)
--- a/src/testdir/test_ins_complete.vim
+++ b/src/testdir/test_ins_complete.vim
@@ -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
--- a/src/testdir/test_tagfunc.vim
+++ b/src/testdir/test_tagfunc.vim
@@ -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
--- a/src/testdir/test_tagjump.vim
+++ b/src/testdir/test_tagjump.vim
@@ -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
--- a/src/testdir/test_taglist.vim
+++ b/src/testdir/test_taglist.vim
@@ -115,3 +115,99 @@ func Test_tagsfile_without_trailing_newl
   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
--- a/src/version.c
+++ b/src/version.c
@@ -743,6 +743,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    88,
+/**/
     87,
 /**/
     86,