To: vim_dev@googlegroups.com Subject: Patch 8.2.0088 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.2.0088 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) 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 *** ../vim-8.2.0087/runtime/doc/tagsrch.txt 2019-12-12 12:49:06.000000000 +0100 --- runtime/doc/tagsrch.txt 2020-01-05 20:28:54.700971203 +0100 *************** *** 344,354 **** 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'. *tag-binary-search* Vim uses binary searching in the tags file to find the desired tag quickly --- 344,354 ---- 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. 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,433 **** 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. *emacs-tags* *emacs_tags* *E430* Emacs style tag files are only supported if Vim was compiled with the --- 426,432 ---- 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'. *emacs-tags* *emacs_tags* *E430* Emacs style tag files are only supported if Vim was compiled with the *** ../vim-8.2.0087/src/tag.c 2020-01-02 14:02:12.316159489 +0100 --- src/tag.c 2020-01-05 20:28:54.700971203 +0100 *************** *** 3808,3813 **** --- 3808,3814 ---- find_extra(char_u **pp) { char_u *str = *pp; + char_u first_char = **pp; // Repeat for addresses separated with ';' for (;;) *************** *** 3817,3823 **** else if (*str == '/' || *str == '?') { str = skip_regexp(str + 1, *str, FALSE, NULL); ! if (*str != **pp) str = NULL; else ++str; --- 3818,3824 ---- else if (*str == '/' || *str == '?') { str = skip_regexp(str + 1, *str, FALSE, NULL); ! if (*str != first_char) str = NULL; else ++str; *************** *** 3837,3842 **** --- 3838,3844 ---- || !(VIM_ISDIGIT(str[1]) || str[1] == '/' || str[1] == '?')) break; ++str; // skip ';' + first_char = *str; } if (str != NULL && STRNCMP(str, ";\"", 2) == 0) *** ../vim-8.2.0087/src/testdir/test_ins_complete.vim 2020-01-04 14:32:35.522717984 +0100 --- src/testdir/test_ins_complete.vim 2020-01-05 20:28:54.700971203 +0100 *************** *** 432,434 **** --- 432,462 ---- 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\\\\" + call assert_equal('int second() {}', getline(1)) + set noshowfulltag + + call delete('Xtags') + call delete('Xfoo') + set tags& + %bwipe! + endfunc *** ../vim-8.2.0087/src/testdir/test_tagfunc.vim 2019-04-28 17:48:10.000000000 +0200 --- src/testdir/test_tagfunc.vim 2020-01-05 20:28:54.700971203 +0100 *************** *** 81,84 **** --- 81,108 ---- 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 *** ../vim-8.2.0087/src/testdir/test_tagjump.vim 2020-01-02 14:02:12.316159489 +0100 --- src/testdir/test_tagjump.vim 2020-01-05 20:28:54.700971203 +0100 *************** *** 578,581 **** --- 578,870 ---- 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 *** ../vim-8.2.0087/src/testdir/test_taglist.vim 2019-09-08 18:09:15.000000000 +0200 --- src/testdir/test_taglist.vim 2020-01-05 20:28:54.700971203 +0100 *************** *** 115,117 **** --- 115,213 ---- 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 *** ../vim-8.2.0087/src/version.c 2020-01-05 14:38:37.110600924 +0100 --- src/version.c 2020-01-05 20:32:05.072265610 +0100 *************** *** 744,745 **** --- 744,747 ---- { /* Add new patch number below this line */ + /**/ + 88, /**/ -- ARTHUR: No, hang on! Just answer the five questions ... GALAHAD: Three questions ... ARTHUR: Three questions ... And we shall watch ... and pray. "Monty Python and the Holy Grail" PYTHON (MONTY) PICTURES LTD /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\ \\\ an exciting new programming language -- http://www.Zimbu.org /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///