diff options
author | zeertzjq <zeertzjq@outlook.com> | 2024-07-10 10:35:12 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-07-10 10:35:12 +0800 |
commit | 158ffd646d18eb26ca7e04e9cb9110305577b9c8 (patch) | |
tree | 927bad31846421670891666a6829c53a41b9830e | |
parent | 545aafbeb80eb52c182ce139800489b392a12d0d (diff) | |
download | rneovim-158ffd646d18eb26ca7e04e9cb9110305577b9c8.tar.gz rneovim-158ffd646d18eb26ca7e04e9cb9110305577b9c8.tar.bz2 rneovim-158ffd646d18eb26ca7e04e9cb9110305577b9c8.zip |
vim-patch:9.1.0554: :bw leaves jumplist and tagstack data around (#29639)
Problem: :bw leaves jumplist and tagstack data around
(Paul "Joey" Clark)
Solution: Wipe jumplist and tagstack references to the wiped buffer
(LemonBoy)
As documented the :bwipeout command brutally deletes all the references
to the buffer, so let's make it delete all the entries in the jump list
and tag stack referring to the wiped-out buffer.
fixes: vim/vim#8201
closes: vim/vim#15185
https://github.com/vim/vim/commit/4ff3a9b1e3ba45f9dbd0ea8c721f27d9315c4d93
Co-authored-by: LemonBoy <thatlemon@gmail.com>
-rw-r--r-- | runtime/doc/windows.txt | 9 | ||||
-rw-r--r-- | src/nvim/buffer.c | 37 | ||||
-rw-r--r-- | src/nvim/mark.c | 51 | ||||
-rw-r--r-- | src/nvim/tag.c | 2 | ||||
-rw-r--r-- | src/nvim/tag.h | 1 | ||||
-rw-r--r-- | test/old/testdir/test_jumplist.vim | 22 | ||||
-rw-r--r-- | test/old/testdir/test_tagjump.vim | 17 | ||||
-rw-r--r-- | test/old/testdir/test_winfixbuf.vim | 1 |
8 files changed, 90 insertions, 50 deletions
diff --git a/runtime/doc/windows.txt b/runtime/doc/windows.txt index 4791e73929..840d8b51af 100644 --- a/runtime/doc/windows.txt +++ b/runtime/doc/windows.txt @@ -1175,11 +1175,12 @@ list of buffers. |unlisted-buffer| :bw[ipeout][!] N1 N2 ... Like |:bdelete|, but really delete the buffer. Everything related to the buffer is lost. All marks in this buffer - become invalid, option settings are lost, etc. Don't use this + become invalid, option settings are lost, the jumplist and + tagstack data will be purged, etc. Don't use this unless you know what you are doing. Examples: > - :.+,$bwipeout " wipe out all buffers after the current - " one - :%bwipeout " wipe out all buffers + :.+,$bwipeout " wipe out all buffers after the current + " one + :%bwipeout " wipe out all buffers < :[N]bun[load][!] *:bun* *:bunload* *E515* :bun[load][!] [N] diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index 3812ff6b44..6878b02a6e 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -699,6 +699,9 @@ bool close_buffer(win_T *win, buf_T *buf, int action, bool abort_if_last, bool i if (buf->b_nwindows > 0) { return false; } + FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { + mark_forget_file(wp, buf->b_fnum); + } if (buf->b_sfname != buf->b_ffname) { XFREE_CLEAR(buf->b_sfname); } else { @@ -1184,32 +1187,6 @@ static int empty_curbuf(bool close_others, int forceit, int action) return retval; } -/// Remove every jump list entry referring to a given buffer. -/// This function will also adjust the current jump list index. -void buf_remove_from_jumplist(buf_T *deleted_buf) -{ - // Remove all jump list entries that match the deleted buffer. - for (int i = curwin->w_jumplistlen - 1; i >= 0; i--) { - buf_T *buf = buflist_findnr(curwin->w_jumplist[i].fmark.fnum); - - if (buf == deleted_buf) { - // Found an entry that we want to delete. - curwin->w_jumplistlen -= 1; - - // If the current jump list index behind the entry we want to - // delete, move it back by one. - if (curwin->w_jumplistidx > i && curwin->w_jumplistidx > 0) { - curwin->w_jumplistidx -= 1; - } - - // Actually remove the entry from the jump list. - for (int d = i; d < curwin->w_jumplistlen; d++) { - curwin->w_jumplist[d] = curwin->w_jumplist[d + 1]; - } - } - } -} - /// Implementation of the commands for the buffer list. /// /// action == DOBUF_GOTO go to specified buffer @@ -1364,6 +1341,8 @@ int do_buffer(int action, int start, int dir, int count, int forceit) } } + int buf_fnum = buf->b_fnum; + // When closing the current buffer stop Visual mode. if (buf == curbuf && VIsual_active) { end_visual_mode(); @@ -1398,7 +1377,7 @@ int do_buffer(int action, int start, int dir, int count, int forceit) if (buf != curbuf) { if (jop_flags & JOP_UNLOAD) { // Remove the buffer to be deleted from the jump list. - buf_remove_from_jumplist(buf); + mark_jumplist_forget_file(curwin, buf_fnum); } close_windows(buf, false); @@ -1423,8 +1402,8 @@ int do_buffer(int action, int start, int dir, int count, int forceit) buf = au_new_curbuf.br_buf; } else if (curwin->w_jumplistlen > 0) { if (jop_flags & JOP_UNLOAD) { - // Remove the current buffer from the jump list. - buf_remove_from_jumplist(curbuf); + // Remove the buffer from the jump list. + mark_jumplist_forget_file(curwin, buf_fnum); } // It's possible that we removed all jump list entries, in that case we need to try another diff --git a/src/nvim/mark.c b/src/nvim/mark.c index 16f444a316..fae28ef6e1 100644 --- a/src/nvim/mark.c +++ b/src/nvim/mark.c @@ -43,6 +43,7 @@ #include "nvim/pos_defs.h" #include "nvim/quickfix.h" #include "nvim/strings.h" +#include "nvim/tag.h" #include "nvim/textobject.h" #include "nvim/types_defs.h" #include "nvim/vim_defs.h" @@ -165,6 +166,56 @@ int setmark_pos(int c, pos_T *pos, int fnum, fmarkv_T *view_pt) return FAIL; } +/// Remove every jump list entry referring to a given buffer. +/// This function will also adjust the current jump list index. +void mark_jumplist_forget_file(win_T *wp, int fnum) +{ + // Remove all jump list entries that match the deleted buffer. + for (int i = wp->w_jumplistlen - 1; i >= 0; i--) { + if (wp->w_jumplist[i].fmark.fnum == fnum) { + // Found an entry that we want to delete. + free_xfmark(wp->w_jumplist[i]); + + // If the current jump list index is behind the entry we want to delete, + // move it back by one. + if (wp->w_jumplistidx > i) { + wp->w_jumplistidx--; + } + + // Actually remove the entry from the jump list. + wp->w_jumplistlen--; + memmove(&wp->w_jumplist[i], &wp->w_jumplist[i + 1], + (size_t)(wp->w_jumplistlen - i) * sizeof(wp->w_jumplist[i])); + } + } +} + +/// Delete every entry referring to file "fnum" from both the jumplist and the +/// tag stack. +void mark_forget_file(win_T *wp, int fnum) +{ + mark_jumplist_forget_file(wp, fnum); + + // Remove all tag stack entries that match the deleted buffer. + for (int i = wp->w_tagstacklen - 1; i >= 0; i--) { + if (wp->w_tagstack[i].fmark.fnum == fnum) { + // Found an entry that we want to delete. + tagstack_clear_entry(&wp->w_tagstack[i]); + + // If the current tag stack index is behind the entry we want to delete, + // move it back by one. + if (wp->w_tagstackidx > i) { + wp->w_tagstackidx--; + } + + // Actually remove the entry from the tag stack. + wp->w_tagstacklen--; + memmove(&wp->w_tagstack[i], &wp->w_tagstack[i + 1], + (size_t)(wp->w_tagstacklen - i) * sizeof(wp->w_tagstack[i])); + } + } +} + // Set the previous context mark to the current position and add it to the // jump list. void setpcmark(void) diff --git a/src/nvim/tag.c b/src/nvim/tag.c index 8f6342bfcc..90269c776e 100644 --- a/src/nvim/tag.c +++ b/src/nvim/tag.c @@ -3184,7 +3184,7 @@ static int find_extra(char **pp) // // Free a single entry in a tag stack // -static void tagstack_clear_entry(taggy_T *item) +void tagstack_clear_entry(taggy_T *item) { XFREE_CLEAR(item->tagname); XFREE_CLEAR(item->user_data); diff --git a/src/nvim/tag.h b/src/nvim/tag.h index 42196b44b7..1c6f41050d 100644 --- a/src/nvim/tag.h +++ b/src/nvim/tag.h @@ -1,5 +1,6 @@ #pragma once +#include "nvim/buffer_defs.h" // IWYU pragma: keep #include "nvim/eval/typval_defs.h" // IWYU pragma: keep #include "nvim/ex_cmds_defs.h" // IWYU pragma: keep #include "nvim/option_defs.h" // IWYU pragma: keep diff --git a/test/old/testdir/test_jumplist.vim b/test/old/testdir/test_jumplist.vim index b4dcdad9d6..5cf4f32628 100644 --- a/test/old/testdir/test_jumplist.vim +++ b/test/old/testdir/test_jumplist.vim @@ -68,26 +68,16 @@ endfunc func Test_jumplist_invalid() new clearjumps - " put some randome text - put ='a' - let prev = bufnr('%') + " Put some random text and fill the jump list. + call setline(1, ['foo', 'bar', 'baz']) + normal G + normal gg setl nomodified bufhidden=wipe e XXJumpListBuffer - let bnr = bufnr('%') - " 1) empty jumplist - let expected = [[ - \ {'lnum': 2, 'bufnr': prev, 'col': 0, 'coladd': 0}], 1] - call assert_equal(expected, getjumplist()) + " The jump list is empty as the buffer was wiped out. + call assert_equal([[], 0], getjumplist()) let jumps = execute(':jumps') call assert_equal('>', jumps[-1:]) - " now jump back - exe ":norm! \<c-o>" - let expected = [[ - \ {'lnum': 2, 'bufnr': prev, 'col': 0, 'coladd': 0}, - \ {'lnum': 1, 'bufnr': bnr, 'col': 0, 'coladd': 0}], 0] - call assert_equal(expected, getjumplist()) - let jumps = execute(':jumps') - call assert_match('> 0 2 0 -invalid-', jumps) endfunc " Test for '' mark in an empty buffer diff --git a/test/old/testdir/test_tagjump.vim b/test/old/testdir/test_tagjump.vim index a614c19ce2..b715aedde6 100644 --- a/test/old/testdir/test_tagjump.vim +++ b/test/old/testdir/test_tagjump.vim @@ -1001,6 +1001,23 @@ func Test_tag_stack() call settagstack(1, {'items' : []}) call assert_fails('pop', 'E73:') + " References to wiped buffer are deleted. + for i in range(10, 20) + edit Xtest + exe "tag var" .. i + endfor + edit Xtest + + let t = gettagstack() + call assert_equal(11, t.length) + call assert_equal(12, t.curidx) + + bwipe! + + let t = gettagstack() + call assert_equal(0, t.length) + call assert_equal(1, t.curidx) + set tags& %bwipe endfunc diff --git a/test/old/testdir/test_winfixbuf.vim b/test/old/testdir/test_winfixbuf.vim index b41eaf3c9b..1777bec184 100644 --- a/test/old/testdir/test_winfixbuf.vim +++ b/test/old/testdir/test_winfixbuf.vim @@ -2934,6 +2934,7 @@ func Test_tfirst() \ "Xtags", 'D') call writefile(["one", "two", "three"], "Xfile", 'D') call writefile(["one"], "Xother", 'D') + tag one edit Xother set winfixbuf |