From e7de891f27d58de9e05b33d50f6fb6bed2db582d Mon Sep 17 00:00:00 2001 From: Yuri Gribov Date: Sat, 21 Jun 2025 05:16:43 +0000 Subject: [PATCH 1/3] adds option to control whether tagstack is copied to new window fixes: #5742 --- runtime/doc/options.txt | 5 ++++ src/option.h | 1 + src/optiondefs.h | 3 ++ src/testdir/Make_all.mak | 2 ++ src/testdir/test_copytagstack.vim | 46 +++++++++++++++++++++++++++++++ src/window.c | 21 ++++++++------ 6 files changed, 69 insertions(+), 9 deletions(-) create mode 100644 src/testdir/test_copytagstack.vim diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index 1f2f400f6d..068736e619 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -2356,6 +2356,11 @@ A jump table for the options with a short description can be found at |Q_op|. NOTE: This option is reset when 'compatible' is set. Also see 'preserveindent'. + *'copytagstack'* *'cptgst'* *'nocopytagstack'* *'nocptgst'* +'copytagstack' 'cptgst' boolean (default: on) + global + Copy tag stack when splitting window. + *'cpoptions'* *'cpo'* *cpo* 'cpoptions' 'cpo' string (Vim default: "aABceFsz", Vi default: all flags, except "#{|&/\." diff --git a/src/option.h b/src/option.h index 740f6eed56..6599652c21 100644 --- a/src/option.h +++ b/src/option.h @@ -422,6 +422,7 @@ EXTERN char_u *p_ofu; // 'omnifunc' EXTERN char_u *p_tsrfu; // 'thesaurusfunc' #endif EXTERN int p_ci; // 'copyindent' +EXTERN int p_cptgst; // 'copytagstack' #if defined(FEAT_GUI) && defined(MACOS_X) EXTERN int *p_antialias; // 'antialias' #endif diff --git a/src/optiondefs.h b/src/optiondefs.h index 5d9f388aa2..f1d32d88c6 100644 --- a/src/optiondefs.h +++ b/src/optiondefs.h @@ -727,6 +727,9 @@ static struct vimoption options[] = {"copyindent", "ci", P_BOOL|P_VI_DEF|P_VIM, (char_u *)&p_ci, PV_CI, NULL, NULL, {(char_u *)FALSE, (char_u *)0L} SCTX_INIT}, + {"copytagstack", "cptgst", P_BOOL|P_VIM, + (char_u *)&p_cptgst, PV_NONE, NULL, NULL, + {(char_u *)TRUE, (char_u *)TRUE} SCTX_INIT}, {"cpoptions", "cpo", P_STRING|P_VIM|P_RALL|P_FLAGLIST, (char_u *)&p_cpo, PV_NONE, did_set_cpoptions, expand_set_cpoptions, {(char_u *)CPO_VI, (char_u *)CPO_VIM} diff --git a/src/testdir/Make_all.mak b/src/testdir/Make_all.mak index ef86a7d6be..f808d1594c 100644 --- a/src/testdir/Make_all.mak +++ b/src/testdir/Make_all.mak @@ -114,6 +114,7 @@ NEW_TESTS = \ test_compiler \ test_conceal \ test_const \ + test_copytagstack \ test_cpoptions \ test_crash \ test_crypt \ @@ -398,6 +399,7 @@ NEW_TESTS_RES = \ test_comparators.res \ test_conceal.res \ test_const.res \ + test_copytagstack.res \ test_cpoptions.res \ test_crash.res \ test_crypt.res \ diff --git a/src/testdir/test_copytagstack.vim b/src/testdir/test_copytagstack.vim new file mode 100644 index 0000000000..191c3eadf1 --- /dev/null +++ b/src/testdir/test_copytagstack.vim @@ -0,0 +1,46 @@ +" test 'copytagstack' option + +source check.vim +source view_util.vim + +func Test_copytagstack() + call writefile(["int Foo;"], 'file.c', 'D') + call writefile(["Foo\tfile.c\t1"], 'Xtags', 'D') + set tags=Xtags + + tag Foo + + let nr0 = winnr() + call assert_equal(1, gettagstack(nr0)['length']) + + split Xtext + + let nr1 = winnr() + call assert_equal(1, gettagstack(nr1)['length']) + + set tags& + bwipe +endfunc + +func Test_nocopytagstack() + call writefile(["int Foo;"], 'file.c', 'D') + call writefile(["Foo\tfile.c\t1"], 'Xtags', 'D') + set tags=Xtags + set nocopytagstack + + tag Foo + + let nr0 = winnr() + call assert_equal(1, gettagstack(nr0)['length']) + + split Xtext + + let nr1 = winnr() + call assert_equal(0, gettagstack(nr1)['length']) + + set tags& + set copytagstack& + bwipe +endfunc + +" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/window.c b/src/window.c index 3d2528fa6d..d01af8187e 100644 --- a/src/window.c +++ b/src/window.c @@ -1559,17 +1559,20 @@ win_init(win_T *newp, win_T *oldp, int flags UNUSED) } // copy tagstack and folds - for (i = 0; i < oldp->w_tagstacklen; i++) + if (p_cptgst) { - taggy_T *tag = &newp->w_tagstack[i]; - *tag = oldp->w_tagstack[i]; - if (tag->tagname != NULL) - tag->tagname = vim_strsave(tag->tagname); - if (tag->user_data != NULL) - tag->user_data = vim_strsave(tag->user_data); + for (i = 0; i < oldp->w_tagstacklen; i++) + { + taggy_T *tag = &newp->w_tagstack[i]; + *tag = oldp->w_tagstack[i]; + if (tag->tagname != NULL) + tag->tagname = vim_strsave(tag->tagname); + if (tag->user_data != NULL) + tag->user_data = vim_strsave(tag->user_data); + } + newp->w_tagstackidx = oldp->w_tagstackidx; + newp->w_tagstacklen = oldp->w_tagstacklen; } - newp->w_tagstackidx = oldp->w_tagstackidx; - newp->w_tagstacklen = oldp->w_tagstacklen; // Keep same changelist position in new window. newp->w_changelistidx = oldp->w_changelistidx; From 4726231a850ce8d04cc0734fd2bf7f723c00ed3f Mon Sep 17 00:00:00 2001 From: Yuri Gribov Date: Fri, 27 Jun 2025 02:49:18 +0000 Subject: [PATCH 2/3] Address review comments. --- runtime/doc/options.txt | 11 ++++---- runtime/doc/tags | 4 +++ runtime/doc/tagsrch.txt | 12 ++++++++ src/option.h | 2 +- src/optiondefs.h | 6 ++-- src/testdir/Make_all.mak | 2 -- src/testdir/test_copytagstack.vim | 46 ------------------------------- src/testdir/test_tagjump.vim | 40 +++++++++++++++++++++++++++ src/window.c | 2 +- 9 files changed, 67 insertions(+), 58 deletions(-) delete mode 100644 src/testdir/test_copytagstack.vim diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index 068736e619..d22007df65 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -2356,11 +2356,6 @@ A jump table for the options with a short description can be found at |Q_op|. NOTE: This option is reset when 'compatible' is set. Also see 'preserveindent'. - *'copytagstack'* *'cptgst'* *'nocopytagstack'* *'nocptgst'* -'copytagstack' 'cptgst' boolean (default: on) - global - Copy tag stack when splitting window. - *'cpoptions'* *'cpo'* *cpo* 'cpoptions' 'cpo' string (Vim default: "aABceFsz", Vi default: all flags, except "#{|&/\." @@ -8709,6 +8704,12 @@ A jump table for the options with a short description can be found at |Q_op|. Resetting this option is useful when using a ":tag" command in a mapping which should not change the tagstack. + *'tagstackcopy'* *'tgstcp'* *'notagstackcopy'* *'notgstcp'* +'tagstackcopy' 'tgstcp' boolean (default: on) + global + If on, the tagstack is copied from original windows when creating a + new one (e.g. via :split). When off, new windows start afresh. + *'tcldll'* 'tcldll' string (default depends on the build) global diff --git a/runtime/doc/tags b/runtime/doc/tags index afa4a058f4..ee0f5029cb 100644 --- a/runtime/doc/tags +++ b/runtime/doc/tags @@ -765,6 +765,7 @@ $quote eval.txt /*$quote* 'notagbsearch' options.txt /*'notagbsearch'* 'notagrelative' options.txt /*'notagrelative'* 'notagstack' options.txt /*'notagstack'* +'notagstackcopy' options.txt /*'notagstackcopy'* 'notbi' options.txt /*'notbi'* 'notbidi' options.txt /*'notbidi'* 'notbs' options.txt /*'notbs'* @@ -776,6 +777,7 @@ $quote eval.txt /*$quote* 'notf' options.txt /*'notf'* 'notgc' options.txt /*'notgc'* 'notgst' options.txt /*'notgst'* +'notgstcp' options.txt /*'notgstcp'* 'notildeop' options.txt /*'notildeop'* 'notimeout' options.txt /*'notimeout'* 'notitle' options.txt /*'notitle'* @@ -1196,6 +1198,7 @@ $quote eval.txt /*$quote* 'tagrelative' options.txt /*'tagrelative'* 'tags' options.txt /*'tags'* 'tagstack' options.txt /*'tagstack'* +'tagstackcopy' options.txt /*'tagstackcopy'* 'tal' options.txt /*'tal'* 'tb' options.txt /*'tb'* 'tbi' options.txt /*'tbi'* @@ -1222,6 +1225,7 @@ $quote eval.txt /*$quote* 'tfu' options.txt /*'tfu'* 'tgc' options.txt /*'tgc'* 'tgst' options.txt /*'tgst'* +'tgstcp' options.txt /*'tgstcp'* 'thesaurus' options.txt /*'thesaurus'* 'thesaurusfunc' options.txt /*'thesaurusfunc'* 'tildeop' options.txt /*'tildeop'* diff --git a/runtime/doc/tagsrch.txt b/runtime/doc/tagsrch.txt index 0717f2ffd6..5b75474636 100644 --- a/runtime/doc/tagsrch.txt +++ b/runtime/doc/tagsrch.txt @@ -185,6 +185,18 @@ commands explained above the tag stack will look like this: The |gettagstack()| function returns the tag stack of a specified window. The |settagstack()| function modifies the tag stack of a window. +In Vim, the tag stack is local to each window. When a new tab or window is +opened (e.g., via |:split| or |:tabnew|), the tag stack is copied from the +original window. This allows you to continue tag navigation seamlessly in the +new window. + +If you prefer not to copy the tag stack when splitting windows, set the +'tagstackcopy' option to off. + +Tag navigation commands (such as :tag, :pop, and CTRL-]) affect only the tag +stack of the current window. Each window maintains its own independent tag +stack. + *tagstack-examples* Write to the tag stack just like `:tag` but with a user-defined jumper#jump_to_tag function: > diff --git a/src/option.h b/src/option.h index 6599652c21..9cdbd06761 100644 --- a/src/option.h +++ b/src/option.h @@ -422,7 +422,6 @@ EXTERN char_u *p_ofu; // 'omnifunc' EXTERN char_u *p_tsrfu; // 'thesaurusfunc' #endif EXTERN int p_ci; // 'copyindent' -EXTERN int p_cptgst; // 'copytagstack' #if defined(FEAT_GUI) && defined(MACOS_X) EXTERN int *p_antialias; // 'antialias' #endif @@ -1014,6 +1013,7 @@ EXTERN long p_tl; // 'taglength' EXTERN int p_tr; // 'tagrelative' EXTERN char_u *p_tags; // 'tags' EXTERN int p_tgst; // 'tagstack' +EXTERN int p_tgstcp; // 'tagstackcopy' #if defined(DYNAMIC_TCL) EXTERN char_u *p_tcldll; // 'tcldll' #endif diff --git a/src/optiondefs.h b/src/optiondefs.h index f1d32d88c6..a87fe1bc97 100644 --- a/src/optiondefs.h +++ b/src/optiondefs.h @@ -727,9 +727,6 @@ static struct vimoption options[] = {"copyindent", "ci", P_BOOL|P_VI_DEF|P_VIM, (char_u *)&p_ci, PV_CI, NULL, NULL, {(char_u *)FALSE, (char_u *)0L} SCTX_INIT}, - {"copytagstack", "cptgst", P_BOOL|P_VIM, - (char_u *)&p_cptgst, PV_NONE, NULL, NULL, - {(char_u *)TRUE, (char_u *)TRUE} SCTX_INIT}, {"cpoptions", "cpo", P_STRING|P_VIM|P_RALL|P_FLAGLIST, (char_u *)&p_cpo, PV_NONE, did_set_cpoptions, expand_set_cpoptions, {(char_u *)CPO_VI, (char_u *)CPO_VIM} @@ -2589,6 +2586,9 @@ static struct vimoption options[] = {"tagstack", "tgst", P_BOOL|P_VI_DEF, (char_u *)&p_tgst, PV_NONE, NULL, NULL, {(char_u *)TRUE, (char_u *)0L} SCTX_INIT}, + {"tagstackcopy", "tgstcp", P_BOOL|P_VIM, + (char_u *)&p_tgstcp, PV_NONE, NULL, NULL, + {(char_u *)TRUE, (char_u *)TRUE} SCTX_INIT}, {"tcldll", NULL, P_STRING|P_EXPAND|P_VI_DEF|P_SECURE, #if defined(DYNAMIC_TCL) (char_u *)&p_tcldll, PV_NONE, NULL, NULL, diff --git a/src/testdir/Make_all.mak b/src/testdir/Make_all.mak index f808d1594c..ef86a7d6be 100644 --- a/src/testdir/Make_all.mak +++ b/src/testdir/Make_all.mak @@ -114,7 +114,6 @@ NEW_TESTS = \ test_compiler \ test_conceal \ test_const \ - test_copytagstack \ test_cpoptions \ test_crash \ test_crypt \ @@ -399,7 +398,6 @@ NEW_TESTS_RES = \ test_comparators.res \ test_conceal.res \ test_const.res \ - test_copytagstack.res \ test_cpoptions.res \ test_crash.res \ test_crypt.res \ diff --git a/src/testdir/test_copytagstack.vim b/src/testdir/test_copytagstack.vim deleted file mode 100644 index 191c3eadf1..0000000000 --- a/src/testdir/test_copytagstack.vim +++ /dev/null @@ -1,46 +0,0 @@ -" test 'copytagstack' option - -source check.vim -source view_util.vim - -func Test_copytagstack() - call writefile(["int Foo;"], 'file.c', 'D') - call writefile(["Foo\tfile.c\t1"], 'Xtags', 'D') - set tags=Xtags - - tag Foo - - let nr0 = winnr() - call assert_equal(1, gettagstack(nr0)['length']) - - split Xtext - - let nr1 = winnr() - call assert_equal(1, gettagstack(nr1)['length']) - - set tags& - bwipe -endfunc - -func Test_nocopytagstack() - call writefile(["int Foo;"], 'file.c', 'D') - call writefile(["Foo\tfile.c\t1"], 'Xtags', 'D') - set tags=Xtags - set nocopytagstack - - tag Foo - - let nr0 = winnr() - call assert_equal(1, gettagstack(nr0)['length']) - - split Xtext - - let nr1 = winnr() - call assert_equal(0, gettagstack(nr1)['length']) - - set tags& - set copytagstack& - bwipe -endfunc - -" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/testdir/test_tagjump.vim b/src/testdir/test_tagjump.vim index f53d9b88f7..1eaa900530 100644 --- a/src/testdir/test_tagjump.vim +++ b/src/testdir/test_tagjump.vim @@ -1671,4 +1671,44 @@ func Test_tag_excmd_with_number_vim9script() bwipe! endfunc +func Test_tagstackcopy() + call writefile(["int Foo;"], 'file.c', 'D') + call writefile(["Foo\tfile.c\t1"], 'Xtags', 'D') + set tags=Xtags + + tag Foo + + let nr0 = winnr() + call assert_equal(1, gettagstack(nr0)['length']) + + split Xtext + + let nr1 = winnr() + call assert_equal(1, gettagstack(nr1)['length']) + + set tags& + bwipe +endfunc + +func Test_notagstackcopy() + call writefile(["int Foo;"], 'file.c', 'D') + call writefile(["Foo\tfile.c\t1"], 'Xtags', 'D') + set tags=Xtags + set notagstackcopy + + tag Foo + + let nr0 = winnr() + call assert_equal(1, gettagstack(nr0)['length']) + + split Xtext + + let nr1 = winnr() + call assert_equal(0, gettagstack(nr1)['length']) + + set tags& + set tagstackcopy& + bwipe +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/window.c b/src/window.c index d01af8187e..2c96e5f933 100644 --- a/src/window.c +++ b/src/window.c @@ -1559,7 +1559,7 @@ win_init(win_T *newp, win_T *oldp, int flags UNUSED) } // copy tagstack and folds - if (p_cptgst) + if (p_tgstcp) { for (i = 0; i < oldp->w_tagstacklen; i++) { From 04830e04897d5e64319b9910ca06e67cd8181b54 Mon Sep 17 00:00:00 2001 From: Yuri Gribov Date: Fri, 27 Jun 2025 03:32:30 +0000 Subject: [PATCH 3/3] Added a cleartagstack function to clear window tagstack. This could be used in autocommands like autocmd WinNew * :call cleartagstack() to auto-clear tagstacks of new windows. --- runtime/doc/builtin.txt | 11 +++++++++++ runtime/doc/tagsrch.txt | 6 +++++- runtime/doc/usr_41.txt | 1 + runtime/syntax/vim.vim | 2 +- src/evalfunc.c | 27 +++++++++++++++++++++++++++ src/proto/tag.pro | 1 + src/tag.c | 8 +++++--- src/testdir/test_tagjump.vim | 18 ++++++++++++++++++ 8 files changed, 69 insertions(+), 5 deletions(-) diff --git a/runtime/doc/builtin.txt b/runtime/doc/builtin.txt index 8c79f565bf..9198b350fb 100644 --- a/runtime/doc/builtin.txt +++ b/runtime/doc/builtin.txt @@ -129,6 +129,7 @@ charidx({string}, {idx} [, {countcc} [, {utf16}]]) chdir({dir}) String change current working directory cindent({lnum}) Number C indent for line {lnum} clearmatches([{win}]) none clear all matches +cleartagstack([{nr}]) Number clear tag stack in window {nr} cmdcomplete_info() Dict get current cmdline completion information col({expr} [, {winid}]) Number column byte index of cursor or mark @@ -1834,6 +1835,16 @@ clearmatches([{win}]) *clearmatches()* Return type: |Number| +cleartagstack([{nr}]) cleartagstack() + Clear the tag stack of the window {nr}. + {nr} can be the window number or the |window-ID|. + When {winnr} is not specified, the current window is used. + + Returns zero for success, -1 for failure. + + Return type: |Number| + + cmdcomplete_info() *cmdcomplete_info()* Returns a |Dictionary| with information about cmdline completion. See |cmdline-completion|. diff --git a/runtime/doc/tagsrch.txt b/runtime/doc/tagsrch.txt index 5b75474636..3288ed3083 100644 --- a/runtime/doc/tagsrch.txt +++ b/runtime/doc/tagsrch.txt @@ -183,7 +183,8 @@ commands explained above the tag stack will look like this: 2 1 FuncB 59 harddisk2:text/vim/src/main.c The |gettagstack()| function returns the tag stack of a specified window. The -|settagstack()| function modifies the tag stack of a window. +|settagstack()| function modifies the tag stack of a window and the +cleartagstack() clears it. In Vim, the tag stack is local to each window. When a new tab or window is opened (e.g., via |:split| or |:tabnew|), the tag stack is copied from the @@ -220,6 +221,9 @@ Push a new item onto the tag stack: > let newtag = [{'tagname' : 'mytag', 'from' : pos}] call settagstack(2, {'items' : newtag}, 'a') < +Finally clear the stack: + call cleartagstack(2) + *E73* When you try to use the tag stack while it doesn't contain anything you will get an error message. diff --git a/runtime/doc/usr_41.txt b/runtime/doc/usr_41.txt index ffa6fcb7ae..be95e50971 100644 --- a/runtime/doc/usr_41.txt +++ b/runtime/doc/usr_41.txt @@ -1382,6 +1382,7 @@ Timers: *timer-functions* Tags: *tag-functions* taglist() get list of matching tags tagfiles() get a list of tags files + cleartagstack() clear the tag stack of a window gettagstack() get the tag stack of a window settagstack() modify the tag stack of a window diff --git a/runtime/syntax/vim.vim b/runtime/syntax/vim.vim index 71d7f37ef6..8666b3c8b1 100644 --- a/runtime/syntax/vim.vim +++ b/runtime/syntax/vim.vim @@ -136,7 +136,7 @@ syn case match " Function Names {{{2 " GEN_SYN_VIM: vimFuncName, START_STR='syn keyword vimFuncName contained', END_STR='' syn keyword vimFuncName contained abs acos add and append appendbufline argc argidx arglistid argv asin assert_beeps assert_equal assert_equalfile assert_exception assert_fails assert_false assert_inrange assert_match assert_nobeep assert_notequal assert_notmatch assert_report assert_true atan atan2 autocmd_add autocmd_delete autocmd_get balloon_gettext balloon_show balloon_split base64_decode base64_encode bindtextdomain blob2list blob2str browse browsedir bufadd bufexists buflisted bufload bufloaded bufname bufnr bufwinid bufwinnr byte2line byteidx byteidxcomp call ceil ch_canread ch_close ch_close_in ch_evalexpr ch_evalraw ch_getbufnr ch_getjob ch_info ch_log ch_logfile ch_open ch_read ch_readblob ch_readraw ch_sendexpr ch_sendraw ch_setoptions ch_status changenr -syn keyword vimFuncName contained char2nr charclass charcol charidx chdir cindent clearmatches cmdcomplete_info col complete complete_add complete_check complete_info complete_match confirm copy cos cosh count cscope_connection cursor debugbreak deepcopy delete deletebufline did_filetype diff diff_filler diff_hlID digraph_get digraph_getlist digraph_set digraph_setlist echoraw empty environ err_teapot escape eval eventhandler executable execute exepath exists exists_compiled exp expand expandcmd extend extendnew feedkeys filecopy filereadable filewritable filter finddir findfile flatten flattennew float2nr floor fmod fnameescape fnamemodify foldclosed foldclosedend foldlevel foldtext foldtextresult foreach foreground fullcommand funcref function garbagecollect +syn keyword vimFuncName contained char2nr charclass charcol charidx chdir cindent clearmatches cleartagstack cmdcomplete_info col complete complete_add complete_check complete_info complete_match confirm copy cos cosh count cscope_connection cursor debugbreak deepcopy delete deletebufline did_filetype diff diff_filler diff_hlID digraph_get digraph_getlist digraph_set digraph_setlist echoraw empty environ err_teapot escape eval eventhandler executable execute exepath exists exists_compiled exp expand expandcmd extend extendnew feedkeys filecopy filereadable filewritable filter finddir findfile flatten flattennew float2nr floor fmod fnameescape fnamemodify foldclosed foldclosedend foldlevel foldtext foldtextresult foreach foreground fullcommand funcref function garbagecollect syn keyword vimFuncName contained get getbufinfo getbufline getbufoneline getbufvar getcellpixels getcellwidths getchangelist getchar getcharmod getcharpos getcharsearch getcharstr getcmdcomplpat getcmdcompltype getcmdline getcmdpos getcmdprompt getcmdscreenpos getcmdtype getcmdwintype getcompletion getcurpos getcursorcharpos getcwd getenv getfontname getfperm getfsize getftime getftype getimstatus getjumplist getline getloclist getmarklist getmatches getmousepos getmouseshape getpid getpos getqflist getreg getreginfo getregion getregionpos getregtype getscriptinfo getstacktrace gettabinfo gettabvar gettabwinvar gettagstack gettext getwininfo getwinpos getwinposx getwinposy getwinvar glob glob2regpat globpath has has_key haslocaldir hasmapto histadd histdel histget syn keyword vimFuncName contained histnr hlID hlexists hlget hlset hostname iconv id indent index indexof input inputdialog inputlist inputrestore inputsave inputsecret insert instanceof interrupt invert isabsolutepath isdirectory isinf islocked isnan items job_getchannel job_info job_setoptions job_start job_status job_stop join js_decode js_encode json_decode json_encode keys keytrans len libcall libcallnr line line2byte lispindent list2blob list2str list2tuple listener_add listener_flush listener_remove localtime log log10 luaeval map maparg mapcheck maplist mapnew mapset match matchadd matchaddpos matcharg matchbufline matchdelete matchend matchfuzzy matchfuzzypos matchlist matchstr matchstrlist matchstrpos max menu_info min mkdir mode mzeval nextnonblank syn keyword vimFuncName contained ngettext nr2char or pathshorten perleval popup_atcursor popup_beval popup_clear popup_close popup_create popup_dialog popup_filter_menu popup_filter_yesno popup_findecho popup_findinfo popup_findpreview popup_getoptions popup_getpos popup_hide popup_list popup_locate popup_menu popup_move popup_notification popup_setbuf popup_setoptions popup_settext popup_show pow prevnonblank printf prompt_getprompt prompt_setcallback prompt_setinterrupt prompt_setprompt prop_add prop_add_list prop_clear prop_find prop_list prop_remove prop_type_add prop_type_change prop_type_delete prop_type_get prop_type_list pum_getpos pumvisible py3eval pyeval pyxeval rand range readblob readdir readdirex readfile reduce reg_executing reg_recording reltime diff --git a/src/evalfunc.c b/src/evalfunc.c index 76955a7ed0..d3db0daff3 100644 --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -36,6 +36,7 @@ static void f_call(typval_T *argvars, typval_T *rettv); static void f_changenr(typval_T *argvars, typval_T *rettv); static void f_char2nr(typval_T *argvars, typval_T *rettv); static void f_charcol(typval_T *argvars, typval_T *rettv); +static void f_cleartagstack(typval_T *argvars, typval_T *rettv); static void f_col(typval_T *argvars, typval_T *rettv); static void f_confirm(typval_T *argvars, typval_T *rettv); static void f_copy(typval_T *argvars, typval_T *rettv); @@ -2092,6 +2093,8 @@ static funcentry_T global_functions[] = ret_number, f_cindent}, {"clearmatches", 0, 1, FEARG_1, arg1_number, ret_void, f_clearmatches}, + {"cleartagstack", 0, 1, FEARG_1, arg1_number, + ret_number_bool, f_cleartagstack}, {"cmdcomplete_info",0, 0, 0, NULL, ret_dict_any, f_cmdcomplete_info}, {"col", 1, 2, FEARG_1, arg2_string_or_list_number, @@ -4096,6 +4099,30 @@ get_optional_window(typval_T *argvars, int idx) return win; } +/* + * "cleartagstack()" function + */ + static void +f_cleartagstack(typval_T *argvars, typval_T *rettv) +{ + win_T *wp = curwin; // default is current window + + rettv->vval.v_number = -1; + + if (in_vim9script() && check_for_opt_number_arg(argvars, 0) == FAIL) + return; + + if (argvars[0].v_type != VAR_UNKNOWN) + { + wp = find_win_by_nr_or_id(&argvars[0]); + if (wp == NULL) + return; + } + + if (clear_tagstack(wp) == OK) + rettv->vval.v_number = 0; +} + /* * "col(string)" function */ diff --git a/src/proto/tag.pro b/src/proto/tag.pro index 4ab2e00db6..812e840310 100644 --- a/src/proto/tag.pro +++ b/src/proto/tag.pro @@ -14,5 +14,6 @@ void tagstack_clear_entry(taggy_T *item); int expand_tags(int tagnames, char_u *pat, int *num_file, char_u ***file); int get_tags(list_T *list, char_u *pat, char_u *buf_fname); void get_tagstack(win_T *wp, dict_T *retdict); +int clear_tagstack(win_T *wp); int set_tagstack(win_T *wp, dict_T *d, int action); /* vim: set ft=c : */ diff --git a/src/tag.c b/src/tag.c index 6912e87433..e9099ca6f4 100644 --- a/src/tag.c +++ b/src/tag.c @@ -4545,8 +4545,8 @@ get_tagstack(win_T *wp, dict_T *retdict) /* * Free all the entries in the tag stack of the specified window */ - static void -tagstack_clear(win_T *wp) + int +clear_tagstack(win_T *wp) { int i; @@ -4555,6 +4555,8 @@ tagstack_clear(win_T *wp) tagstack_clear_entry(&wp->w_tagstack[i]); wp->w_tagstacklen = 0; wp->w_tagstackidx = 0; + + return OK; } /* @@ -4709,7 +4711,7 @@ set_tagstack(win_T *wp, dict_T *d, int action) if (l != NULL) { if (action == 'r') // replace the stack - tagstack_clear(wp); + clear_tagstack(wp); tagstack_push_items(wp, l); // set the current index after the last entry diff --git a/src/testdir/test_tagjump.vim b/src/testdir/test_tagjump.vim index 1eaa900530..14e44eb36d 100644 --- a/src/testdir/test_tagjump.vim +++ b/src/testdir/test_tagjump.vim @@ -499,6 +499,23 @@ func Test_getsettagstack() set tags& endfunc +func Test_cleartagstack() + call writefile(["int Foo;"], 'file.c', 'D') + call writefile(["Foo\tfile.c\t1"], 'Xtags', 'D') + set tags=Xtags + + tag Foo + + let nr0 = winnr() + call assert_equal(1, gettagstack(nr0)['length']) + + call cleartagstack(nr0) + call assert_equal(0, gettagstack(nr0).length) + + set tags& + bwipe +endfunc + func Test_tag_with_count() call writefile([ \ 'test Xtest.h /^void test();$/;" p typeref:typename:void signature:()', @@ -1153,6 +1170,7 @@ func Test_multimatch_non_existing_files() set tags& %bwipe + call cleartagstack() " FIXME: why tagstack is not empty here ? endfunc func Test_tselect_listing()