mirror of
https://github.com/neovim/neovim.git
synced 2025-09-06 03:18:16 +00:00
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
4ff3a9b1e3
Co-authored-by: LemonBoy <thatlemon@gmail.com>
This commit is contained in:
@@ -1175,11 +1175,12 @@ list of buffers. |unlisted-buffer|
|
|||||||
:bw[ipeout][!] N1 N2 ...
|
:bw[ipeout][!] N1 N2 ...
|
||||||
Like |:bdelete|, but really delete the buffer. Everything
|
Like |:bdelete|, but really delete the buffer. Everything
|
||||||
related to the buffer is lost. All marks in this buffer
|
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: >
|
unless you know what you are doing. Examples: >
|
||||||
:.+,$bwipeout " wipe out all buffers after the current
|
:.+,$bwipeout " wipe out all buffers after the current
|
||||||
" one
|
" one
|
||||||
:%bwipeout " wipe out all buffers
|
:%bwipeout " wipe out all buffers
|
||||||
<
|
<
|
||||||
:[N]bun[load][!] *:bun* *:bunload* *E515*
|
:[N]bun[load][!] *:bun* *:bunload* *E515*
|
||||||
:bun[load][!] [N]
|
:bun[load][!] [N]
|
||||||
|
@@ -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) {
|
if (buf->b_nwindows > 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
|
||||||
|
mark_forget_file(wp, buf->b_fnum);
|
||||||
|
}
|
||||||
if (buf->b_sfname != buf->b_ffname) {
|
if (buf->b_sfname != buf->b_ffname) {
|
||||||
XFREE_CLEAR(buf->b_sfname);
|
XFREE_CLEAR(buf->b_sfname);
|
||||||
} else {
|
} else {
|
||||||
@@ -1184,32 +1187,6 @@ static int empty_curbuf(bool close_others, int forceit, int action)
|
|||||||
return retval;
|
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.
|
/// Implementation of the commands for the buffer list.
|
||||||
///
|
///
|
||||||
/// action == DOBUF_GOTO go to specified buffer
|
/// 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.
|
// When closing the current buffer stop Visual mode.
|
||||||
if (buf == curbuf && VIsual_active) {
|
if (buf == curbuf && VIsual_active) {
|
||||||
end_visual_mode();
|
end_visual_mode();
|
||||||
@@ -1398,7 +1377,7 @@ int do_buffer(int action, int start, int dir, int count, int forceit)
|
|||||||
if (buf != curbuf) {
|
if (buf != curbuf) {
|
||||||
if (jop_flags & JOP_UNLOAD) {
|
if (jop_flags & JOP_UNLOAD) {
|
||||||
// Remove the buffer to be deleted from the jump list.
|
// 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);
|
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;
|
buf = au_new_curbuf.br_buf;
|
||||||
} else if (curwin->w_jumplistlen > 0) {
|
} else if (curwin->w_jumplistlen > 0) {
|
||||||
if (jop_flags & JOP_UNLOAD) {
|
if (jop_flags & JOP_UNLOAD) {
|
||||||
// Remove the current buffer from the jump list.
|
// Remove the buffer from the jump list.
|
||||||
buf_remove_from_jumplist(curbuf);
|
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
|
// It's possible that we removed all jump list entries, in that case we need to try another
|
||||||
|
@@ -43,6 +43,7 @@
|
|||||||
#include "nvim/pos_defs.h"
|
#include "nvim/pos_defs.h"
|
||||||
#include "nvim/quickfix.h"
|
#include "nvim/quickfix.h"
|
||||||
#include "nvim/strings.h"
|
#include "nvim/strings.h"
|
||||||
|
#include "nvim/tag.h"
|
||||||
#include "nvim/textobject.h"
|
#include "nvim/textobject.h"
|
||||||
#include "nvim/types_defs.h"
|
#include "nvim/types_defs.h"
|
||||||
#include "nvim/vim_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;
|
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
|
// Set the previous context mark to the current position and add it to the
|
||||||
// jump list.
|
// jump list.
|
||||||
void setpcmark(void)
|
void setpcmark(void)
|
||||||
|
@@ -3184,7 +3184,7 @@ static int find_extra(char **pp)
|
|||||||
//
|
//
|
||||||
// Free a single entry in a tag stack
|
// 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->tagname);
|
||||||
XFREE_CLEAR(item->user_data);
|
XFREE_CLEAR(item->user_data);
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "nvim/buffer_defs.h" // IWYU pragma: keep
|
||||||
#include "nvim/eval/typval_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/ex_cmds_defs.h" // IWYU pragma: keep
|
||||||
#include "nvim/option_defs.h" // IWYU pragma: keep
|
#include "nvim/option_defs.h" // IWYU pragma: keep
|
||||||
|
@@ -68,26 +68,16 @@ endfunc
|
|||||||
func Test_jumplist_invalid()
|
func Test_jumplist_invalid()
|
||||||
new
|
new
|
||||||
clearjumps
|
clearjumps
|
||||||
" put some randome text
|
" Put some random text and fill the jump list.
|
||||||
put ='a'
|
call setline(1, ['foo', 'bar', 'baz'])
|
||||||
let prev = bufnr('%')
|
normal G
|
||||||
|
normal gg
|
||||||
setl nomodified bufhidden=wipe
|
setl nomodified bufhidden=wipe
|
||||||
e XXJumpListBuffer
|
e XXJumpListBuffer
|
||||||
let bnr = bufnr('%')
|
" The jump list is empty as the buffer was wiped out.
|
||||||
" 1) empty jumplist
|
call assert_equal([[], 0], getjumplist())
|
||||||
let expected = [[
|
|
||||||
\ {'lnum': 2, 'bufnr': prev, 'col': 0, 'coladd': 0}], 1]
|
|
||||||
call assert_equal(expected, getjumplist())
|
|
||||||
let jumps = execute(':jumps')
|
let jumps = execute(':jumps')
|
||||||
call assert_equal('>', jumps[-1:])
|
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
|
endfunc
|
||||||
|
|
||||||
" Test for '' mark in an empty buffer
|
" Test for '' mark in an empty buffer
|
||||||
|
@@ -1001,6 +1001,23 @@ func Test_tag_stack()
|
|||||||
call settagstack(1, {'items' : []})
|
call settagstack(1, {'items' : []})
|
||||||
call assert_fails('pop', 'E73:')
|
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&
|
set tags&
|
||||||
%bwipe
|
%bwipe
|
||||||
endfunc
|
endfunc
|
||||||
|
@@ -2934,6 +2934,7 @@ func Test_tfirst()
|
|||||||
\ "Xtags", 'D')
|
\ "Xtags", 'D')
|
||||||
call writefile(["one", "two", "three"], "Xfile", 'D')
|
call writefile(["one", "two", "three"], "Xfile", 'D')
|
||||||
call writefile(["one"], "Xother", 'D')
|
call writefile(["one"], "Xother", 'D')
|
||||||
|
tag one
|
||||||
edit Xother
|
edit Xother
|
||||||
|
|
||||||
set winfixbuf
|
set winfixbuf
|
||||||
|
Reference in New Issue
Block a user