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:
zeertzjq
2024-07-10 10:35:12 +08:00
committed by GitHub
parent 545aafbeb8
commit 158ffd646d
8 changed files with 90 additions and 50 deletions

View File

@@ -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

View File

@@ -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)

View File

@@ -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);

View File

@@ -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