Skip to content

Commit

Permalink
feat(jumplist): allow opting out of removing unloaded buffers
Browse files Browse the repository at this point in the history
Problem:  Cannot opt out of removing unloaded buffers from the jumplist.
Solution: Only enable that with "unload" flag in 'jumpoptions'.
  • Loading branch information
zeertzjq committed Jun 15, 2024
1 parent 61aabe0 commit cde2a78
Show file tree
Hide file tree
Showing 8 changed files with 88 additions and 22 deletions.
4 changes: 3 additions & 1 deletion runtime/doc/options.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3619,7 +3619,7 @@ A jump table for the options with a short description can be found at |Q_op|.
Otherwise only one space is inserted.

*'jumpoptions'* *'jop'*
'jumpoptions' 'jop' string (default "")
'jumpoptions' 'jop' string (default "unload")
global
List of words that change the behavior of the |jumplist|.
stack Make the jumplist behave like the tagstack.
Expand All @@ -3632,6 +3632,8 @@ A jump table for the options with a short description can be found at |Q_op|.
|alternate-file| or using |mark-motions| try to
restore the |mark-view| in which the action occurred.

unload Remove unloaded buffers from the jumplist.

*'keymap'* *'kmp'*
'keymap' 'kmp' string (default "")
local to buffer
Expand Down
2 changes: 2 additions & 0 deletions runtime/doc/vim_diff.txt
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ Defaults *nvim-defaults*
- 'isfname' does not include ":" (on Windows). Drive letters are handled
correctly without it. (Use |gF| for filepaths suffixed with ":line:col").
- 'joinspaces' is disabled
- 'jumpoptions' defaults to "unload"
- 'langnoremap' is enabled
- 'langremap' is disabled
- 'laststatus' defaults to 2 (statusline is always shown)
Expand Down Expand Up @@ -341,6 +342,7 @@ string options work.
- 'inccommand' shows interactive results for |:substitute|-like commands
and |:command-preview| commands
- 'jumpoptions' "view" tries to restore the |mark-view| when moving through
"unload" removes unloaded buffer from the jumplist
- the |jumplist|, |changelist|, |alternate-file| or using |mark-motions|.
- 'laststatus' global statusline support
- 'mousescroll' amount to scroll by when scrolling with a mouse
Expand Down
4 changes: 3 additions & 1 deletion runtime/lua/vim/_meta/options.lua

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

47 changes: 30 additions & 17 deletions src/nvim/buffer.c
Original file line number Diff line number Diff line change
Expand Up @@ -1396,8 +1396,10 @@ int do_buffer(int action, int start, int dir, int count, int forceit)

// If the buffer to be deleted is not the current one, delete it here.
if (buf != curbuf) {
// Remove the buffer to be deleted from the jump list.
buf_remove_from_jumplist(buf);
if (jop_flags & JOP_UNLOAD) {
// Remove the buffer to be deleted from the jump list.
buf_remove_from_jumplist(buf);
}

close_windows(buf, false);

Expand All @@ -1420,28 +1422,37 @@ int do_buffer(int action, int start, int dir, int count, int forceit)
if (au_new_curbuf.br_buf != NULL && bufref_valid(&au_new_curbuf)) {
buf = au_new_curbuf.br_buf;
} else if (curwin->w_jumplistlen > 0) {
// Remove the current buffer from the jump list.
buf_remove_from_jumplist(curbuf);
if (jop_flags & JOP_UNLOAD) {
// Remove the current buffer from the jump list.
buf_remove_from_jumplist(curbuf);
}

// It's possible that we removed all jump list entries, in that case we need to try another
// approach
if (curwin->w_jumplistlen > 0) {
// If the index is the same as the length, the current position was not yet added to the jump
// list. So we can safely go back to the last entry and search from there.
if (curwin->w_jumplistidx == curwin->w_jumplistlen) {
curwin->w_jumplistidx = curwin->w_jumplistlen - 1;
}

int jumpidx = curwin->w_jumplistidx;

if (jop_flags & JOP_UNLOAD) {
// If the index is the same as the length, the current position was not yet added to the
// jump list. So we can safely go back to the last entry and search from there.
if (jumpidx == curwin->w_jumplistlen) {
jumpidx = curwin->w_jumplistidx = curwin->w_jumplistlen - 1;
}
} else {
jumpidx--;
if (jumpidx < 0) {
jumpidx = curwin->w_jumplistlen - 1;
}
}

forward = jumpidx;
do {
while ((jop_flags & JOP_UNLOAD) || jumpidx != curwin->w_jumplistidx) {
buf = buflist_findnr(curwin->w_jumplist[jumpidx].fmark.fnum);

if (buf != NULL) {
// Skip unlisted bufs. Also skip a quickfix
// Skip current and unlisted bufs. Also skip a quickfix
// buffer, it might be deleted soon.
if (!buf->b_p_bl || bt_quickfix(buf)) {
if (buf == curbuf || !buf->b_p_bl || bt_quickfix(buf)) {
buf = NULL;
} else if (buf->b_ml.ml_mfp == NULL) {
// skip unloaded buf, but may keep it for later
Expand All @@ -1452,8 +1463,10 @@ int do_buffer(int action, int start, int dir, int count, int forceit)
}
}
if (buf != NULL) { // found a valid buffer: stop searching
curwin->w_jumplistidx = jumpidx;
update_jumplist = false;
if (jop_flags & JOP_UNLOAD) {
curwin->w_jumplistidx = jumpidx;
update_jumplist = false;
}
break;
}
// advance to older entry in jump list
Expand All @@ -1466,7 +1479,7 @@ int do_buffer(int action, int start, int dir, int count, int forceit)
if (jumpidx == forward) { // List exhausted for sure
break;
}
} while (jumpidx != curwin->w_jumplistidx);
}
}
}

Expand Down Expand Up @@ -3728,7 +3741,7 @@ void ex_buffer_all(exarg_T *eap)

// Open the buffer in this window.
swap_exists_action = SEA_DIALOG;
set_curbuf(buf, DOBUF_GOTO, false);
set_curbuf(buf, DOBUF_GOTO, !(jop_flags & JOP_UNLOAD));
if (!bufref_valid(&bufref)) {
// Autocommands deleted the buffer.
swap_exists_action = SEA_NONE;
Expand Down
1 change: 1 addition & 0 deletions src/nvim/option_vars.h
Original file line number Diff line number Diff line change
Expand Up @@ -540,6 +540,7 @@ EXTERN char *p_jop; ///< 'jumpooptions'
EXTERN unsigned jop_flags;
#define JOP_STACK 0x01
#define JOP_VIEW 0x02
#define JOP_UNLOAD 0x04
EXTERN char *p_keymap; ///< 'keymap'
EXTERN char *p_kp; ///< 'keywordprg'
EXTERN char *p_km; ///< 'keymodel'
Expand Down
4 changes: 3 additions & 1 deletion src/nvim/options.lua
Original file line number Diff line number Diff line change
Expand Up @@ -4495,7 +4495,7 @@ return {
{
abbreviation = 'jop',
cb = 'did_set_jumpoptions',
defaults = { if_true = '' },
defaults = { if_true = 'unload' },
deny_duplicates = true,
desc = [=[
List of words that change the behavior of the |jumplist|.
Expand All @@ -4508,6 +4508,8 @@ return {
view When moving through the jumplist, |changelist|,
|alternate-file| or using |mark-motions| try to
restore the |mark-view| in which the action occurred.
unload Remove unloaded buffers from the jumplist.
]=],
expand_cb = 'expand_set_jumpoptions',
full_name = 'jumpoptions',
Expand Down
2 changes: 1 addition & 1 deletion src/nvim/optionstr.c
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ static char *(p_fdc_values[]) = { "auto", "auto:1", "auto:2", "auto:3", "auto:4"
"5", "6", "7", "8", "9", NULL };
static char *(p_spo_values[]) = { "camel", "noplainbuffer", NULL };
static char *(p_icm_values[]) = { "nosplit", "split", NULL };
static char *(p_jop_values[]) = { "stack", "view", NULL };
static char *(p_jop_values[]) = { "stack", "view", "unload", NULL };
static char *(p_tpf_values[]) = { "BS", "HT", "FF", "ESC", "DEL", "C0", "C1", NULL };
static char *(p_rdb_values[]) = { "compositor", "nothrottle", "invalid", "nodelta", "line",
"flush", NULL };
Expand Down
46 changes: 45 additions & 1 deletion test/functional/editor/jump_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ describe("jumpoptions=stack behaves like 'tagstack'", function()
end)
end)

describe('buffer deletion', function()
describe('buffer deletion with jumpoptions+=unload', function()
local base_file = 'Xtest-functional-buffer-deletion'
local file1 = base_file .. '1'
local file2 = base_file .. '2'
Expand Down Expand Up @@ -227,6 +227,12 @@ describe('buffer deletion', function()
command('edit ' .. file3)
end)

after_each(function()
os.remove(file1)
os.remove(file2)
os.remove(file3)
end)

it('deletes jump list entries when the current buffer is deleted', function()
command('edit ' .. file1)

Expand Down Expand Up @@ -319,6 +325,44 @@ describe('buffer deletion', function()
end)
end)

describe('buffer deletion with jumpoptions-=unload', function()
local base_file = 'Xtest-functional-buffer-deletion'
local file1 = base_file .. '1'
local file2 = base_file .. '2'
local base_content = 'text'
local content1 = base_content .. '1'
local content2 = base_content .. '2'

before_each(function()
clear()
command('clearjumps')
command('set jumpoptions-=unload')

write_file(file1, content1, false, false)
write_file(file2, content2, false, false)

command('edit ' .. file1)
command('edit ' .. file2)
end)

after_each(function()
os.remove(file1)
os.remove(file2)
end)

it('Ctrl-O reopens previous buffer with :bunload or :bdelete #28968', function()
eq(file2, fn.bufname(''))
command('bunload')
eq(file1, fn.bufname(''))
feed('<C-O>')
eq(file2, fn.bufname(''))
command('bdelete')
eq(file1, fn.bufname(''))
feed('<C-O>')
eq(file2, fn.bufname(''))
end)
end)

describe('jumpoptions=view', function()
local file1 = 'Xtestfile-functional-editor-jumps'
local file2 = 'Xtestfile-functional-editor-jumps-2'
Expand Down

0 comments on commit cde2a78

Please sign in to comment.