mirror of
				https://github.com/neovim/neovim.git
				synced 2025-11-04 09:44:31 +00:00 
			
		
		
		
	feat(jumplist): allow opting out of removing unloaded buffers (#29347)
Problem: Cannot opt out of removing unloaded buffers from the jumplist. Solution: Only enable that with "unload" flag in 'jumpoptions'.
This commit is contained in:
		@@ -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.
 | 
						Otherwise only one space is inserted.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
						*'jumpoptions'* *'jop'*
 | 
											*'jumpoptions'* *'jop'*
 | 
				
			||||||
'jumpoptions' 'jop'	string	(default "")
 | 
					'jumpoptions' 'jop'	string	(default "unload")
 | 
				
			||||||
			global
 | 
								global
 | 
				
			||||||
	List of words that change the behavior of the |jumplist|.
 | 
						List of words that change the behavior of the |jumplist|.
 | 
				
			||||||
	  stack         Make the jumplist behave like the tagstack.
 | 
						  stack         Make the jumplist behave like the tagstack.
 | 
				
			||||||
@@ -3632,6 +3632,9 @@ A jump table for the options with a short description can be found at |Q_op|.
 | 
				
			|||||||
			|alternate-file| or using |mark-motions| try to
 | 
								|alternate-file| or using |mark-motions| try to
 | 
				
			||||||
			restore the |mark-view| in which the action occurred.
 | 
								restore the |mark-view| in which the action occurred.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						  unload        Remove unloaded buffers from the jumplist.
 | 
				
			||||||
 | 
								EXPERIMENTAL: this flag may change in the future.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
						*'keymap'* *'kmp'*
 | 
											*'keymap'* *'kmp'*
 | 
				
			||||||
'keymap' 'kmp'		string	(default "")
 | 
					'keymap' 'kmp'		string	(default "")
 | 
				
			||||||
			local to buffer
 | 
								local to buffer
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -62,6 +62,7 @@ Defaults                                                    *nvim-defaults*
 | 
				
			|||||||
- 'isfname' does not include ":" (on Windows). Drive letters are handled
 | 
					- 'isfname' does not include ":" (on Windows). Drive letters are handled
 | 
				
			||||||
  correctly without it. (Use |gF| for filepaths suffixed with ":line:col").
 | 
					  correctly without it. (Use |gF| for filepaths suffixed with ":line:col").
 | 
				
			||||||
- 'joinspaces' is disabled
 | 
					- 'joinspaces' is disabled
 | 
				
			||||||
 | 
					- 'jumpoptions' defaults to "unload"
 | 
				
			||||||
- 'langnoremap' is enabled
 | 
					- 'langnoremap' is enabled
 | 
				
			||||||
- 'langremap' is disabled
 | 
					- 'langremap' is disabled
 | 
				
			||||||
- 'laststatus' defaults to 2 (statusline is always shown)
 | 
					- 'laststatus' defaults to 2 (statusline is always shown)
 | 
				
			||||||
@@ -341,6 +342,7 @@ string options work.
 | 
				
			|||||||
- 'inccommand'  shows interactive results for |:substitute|-like commands
 | 
					- 'inccommand'  shows interactive results for |:substitute|-like commands
 | 
				
			||||||
                and |:command-preview| commands
 | 
					                and |:command-preview| commands
 | 
				
			||||||
- 'jumpoptions' "view" tries to restore the |mark-view| when moving through
 | 
					- '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|.
 | 
					- the |jumplist|, |changelist|, |alternate-file| or using |mark-motions|.
 | 
				
			||||||
- 'laststatus'  global statusline support
 | 
					- 'laststatus'  global statusline support
 | 
				
			||||||
- 'mousescroll' amount to scroll by when scrolling with a mouse
 | 
					- 'mousescroll' amount to scroll by when scrolling with a mouse
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										5
									
								
								runtime/lua/vim/_meta/options.lua
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										5
									
								
								runtime/lua/vim/_meta/options.lua
									
									
									
										generated
									
									
									
								
							@@ -3551,8 +3551,11 @@ vim.go.js = vim.go.joinspaces
 | 
				
			|||||||
--- 		|alternate-file` or using `mark-motions` try to
 | 
					--- 		|alternate-file` or using `mark-motions` try to
 | 
				
			||||||
--- 		restore the `mark-view` in which the action occurred.
 | 
					--- 		restore the `mark-view` in which the action occurred.
 | 
				
			||||||
---
 | 
					---
 | 
				
			||||||
 | 
					---   unload        Remove unloaded buffers from the jumplist.
 | 
				
			||||||
 | 
					--- 		EXPERIMENTAL: this flag may change in the future.
 | 
				
			||||||
 | 
					---
 | 
				
			||||||
--- @type string
 | 
					--- @type string
 | 
				
			||||||
vim.o.jumpoptions = ""
 | 
					vim.o.jumpoptions = "unload"
 | 
				
			||||||
vim.o.jop = vim.o.jumpoptions
 | 
					vim.o.jop = vim.o.jumpoptions
 | 
				
			||||||
vim.go.jumpoptions = vim.o.jumpoptions
 | 
					vim.go.jumpoptions = vim.o.jumpoptions
 | 
				
			||||||
vim.go.jop = vim.go.jumpoptions
 | 
					vim.go.jop = vim.go.jumpoptions
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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 the buffer to be deleted is not the current one, delete it here.
 | 
				
			||||||
    if (buf != curbuf) {
 | 
					    if (buf != curbuf) {
 | 
				
			||||||
      // Remove the buffer to be deleted from the jump list.
 | 
					      if (jop_flags & JOP_UNLOAD) {
 | 
				
			||||||
      buf_remove_from_jumplist(buf);
 | 
					        // Remove the buffer to be deleted from the jump list.
 | 
				
			||||||
 | 
					        buf_remove_from_jumplist(buf);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      close_windows(buf, false);
 | 
					      close_windows(buf, false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -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)) {
 | 
					    if (au_new_curbuf.br_buf != NULL && bufref_valid(&au_new_curbuf)) {
 | 
				
			||||||
      buf = au_new_curbuf.br_buf;
 | 
					      buf = au_new_curbuf.br_buf;
 | 
				
			||||||
    } else if (curwin->w_jumplistlen > 0) {
 | 
					    } else if (curwin->w_jumplistlen > 0) {
 | 
				
			||||||
      // Remove the current buffer from the jump list.
 | 
					      if (jop_flags & JOP_UNLOAD) {
 | 
				
			||||||
      buf_remove_from_jumplist(curbuf);
 | 
					        // 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
 | 
					      // It's possible that we removed all jump list entries, in that case we need to try another
 | 
				
			||||||
      // approach
 | 
					      // approach
 | 
				
			||||||
      if (curwin->w_jumplistlen > 0) {
 | 
					      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;
 | 
					        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;
 | 
					        forward = jumpidx;
 | 
				
			||||||
        do {
 | 
					        while ((jop_flags & JOP_UNLOAD) || jumpidx != curwin->w_jumplistidx) {
 | 
				
			||||||
          buf = buflist_findnr(curwin->w_jumplist[jumpidx].fmark.fnum);
 | 
					          buf = buflist_findnr(curwin->w_jumplist[jumpidx].fmark.fnum);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          if (buf != NULL) {
 | 
					          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.
 | 
					            // 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;
 | 
					              buf = NULL;
 | 
				
			||||||
            } else if (buf->b_ml.ml_mfp == NULL) {
 | 
					            } else if (buf->b_ml.ml_mfp == NULL) {
 | 
				
			||||||
              // skip unloaded buf, but may keep it for later
 | 
					              // skip unloaded buf, but may keep it for later
 | 
				
			||||||
@@ -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
 | 
					          if (buf != NULL) {         // found a valid buffer: stop searching
 | 
				
			||||||
            curwin->w_jumplistidx = jumpidx;
 | 
					            if (jop_flags & JOP_UNLOAD) {
 | 
				
			||||||
            update_jumplist = false;
 | 
					              curwin->w_jumplistidx = jumpidx;
 | 
				
			||||||
 | 
					              update_jumplist = false;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
            break;
 | 
					            break;
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
          // advance to older entry in jump list
 | 
					          // advance to older entry in jump list
 | 
				
			||||||
@@ -1466,7 +1479,7 @@ int do_buffer(int action, int start, int dir, int count, int forceit)
 | 
				
			|||||||
          if (jumpidx == forward) {               // List exhausted for sure
 | 
					          if (jumpidx == forward) {               // List exhausted for sure
 | 
				
			||||||
            break;
 | 
					            break;
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
        } while (jumpidx != curwin->w_jumplistidx);
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -3728,7 +3741,7 @@ void ex_buffer_all(exarg_T *eap)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
      // Open the buffer in this window.
 | 
					      // Open the buffer in this window.
 | 
				
			||||||
      swap_exists_action = SEA_DIALOG;
 | 
					      swap_exists_action = SEA_DIALOG;
 | 
				
			||||||
      set_curbuf(buf, DOBUF_GOTO, false);
 | 
					      set_curbuf(buf, DOBUF_GOTO, !(jop_flags & JOP_UNLOAD));
 | 
				
			||||||
      if (!bufref_valid(&bufref)) {
 | 
					      if (!bufref_valid(&bufref)) {
 | 
				
			||||||
        // Autocommands deleted the buffer.
 | 
					        // Autocommands deleted the buffer.
 | 
				
			||||||
        swap_exists_action = SEA_NONE;
 | 
					        swap_exists_action = SEA_NONE;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -540,6 +540,7 @@ EXTERN char *p_jop;             ///< 'jumpooptions'
 | 
				
			|||||||
EXTERN unsigned jop_flags;
 | 
					EXTERN unsigned jop_flags;
 | 
				
			||||||
#define JOP_STACK               0x01
 | 
					#define JOP_STACK               0x01
 | 
				
			||||||
#define JOP_VIEW                0x02
 | 
					#define JOP_VIEW                0x02
 | 
				
			||||||
 | 
					#define JOP_UNLOAD              0x04
 | 
				
			||||||
EXTERN char *p_keymap;          ///< 'keymap'
 | 
					EXTERN char *p_keymap;          ///< 'keymap'
 | 
				
			||||||
EXTERN char *p_kp;              ///< 'keywordprg'
 | 
					EXTERN char *p_kp;              ///< 'keywordprg'
 | 
				
			||||||
EXTERN char *p_km;              ///< 'keymodel'
 | 
					EXTERN char *p_km;              ///< 'keymodel'
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4495,7 +4495,7 @@ return {
 | 
				
			|||||||
    {
 | 
					    {
 | 
				
			||||||
      abbreviation = 'jop',
 | 
					      abbreviation = 'jop',
 | 
				
			||||||
      cb = 'did_set_jumpoptions',
 | 
					      cb = 'did_set_jumpoptions',
 | 
				
			||||||
      defaults = { if_true = '' },
 | 
					      defaults = { if_true = 'unload' },
 | 
				
			||||||
      deny_duplicates = true,
 | 
					      deny_duplicates = true,
 | 
				
			||||||
      desc = [=[
 | 
					      desc = [=[
 | 
				
			||||||
        List of words that change the behavior of the |jumplist|.
 | 
					        List of words that change the behavior of the |jumplist|.
 | 
				
			||||||
@@ -4508,6 +4508,9 @@ return {
 | 
				
			|||||||
          view          When moving through the jumplist, |changelist|,
 | 
					          view          When moving through the jumplist, |changelist|,
 | 
				
			||||||
        		|alternate-file| or using |mark-motions| try to
 | 
					        		|alternate-file| or using |mark-motions| try to
 | 
				
			||||||
        		restore the |mark-view| in which the action occurred.
 | 
					        		restore the |mark-view| in which the action occurred.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          unload        Remove unloaded buffers from the jumplist.
 | 
				
			||||||
 | 
					        		EXPERIMENTAL: this flag may change in the future.
 | 
				
			||||||
      ]=],
 | 
					      ]=],
 | 
				
			||||||
      expand_cb = 'expand_set_jumpoptions',
 | 
					      expand_cb = 'expand_set_jumpoptions',
 | 
				
			||||||
      full_name = 'jumpoptions',
 | 
					      full_name = 'jumpoptions',
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -137,7 +137,7 @@ static char *(p_fdc_values[]) = { "auto", "auto:1", "auto:2", "auto:3", "auto:4"
 | 
				
			|||||||
                                  "5", "6", "7", "8", "9", NULL };
 | 
					                                  "5", "6", "7", "8", "9", NULL };
 | 
				
			||||||
static char *(p_spo_values[]) = { "camel", "noplainbuffer", NULL };
 | 
					static char *(p_spo_values[]) = { "camel", "noplainbuffer", NULL };
 | 
				
			||||||
static char *(p_icm_values[]) = { "nosplit", "split", 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_tpf_values[]) = { "BS", "HT", "FF", "ESC", "DEL", "C0", "C1", NULL };
 | 
				
			||||||
static char *(p_rdb_values[]) = { "compositor", "nothrottle", "invalid", "nodelta", "line",
 | 
					static char *(p_rdb_values[]) = { "compositor", "nothrottle", "invalid", "nodelta", "line",
 | 
				
			||||||
                                  "flush", NULL };
 | 
					                                  "flush", NULL };
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -194,7 +194,7 @@ describe("jumpoptions=stack behaves like 'tagstack'", function()
 | 
				
			|||||||
  end)
 | 
					  end)
 | 
				
			||||||
end)
 | 
					end)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
describe('buffer deletion', function()
 | 
					describe('buffer deletion with jumpoptions+=unload', function()
 | 
				
			||||||
  local base_file = 'Xtest-functional-buffer-deletion'
 | 
					  local base_file = 'Xtest-functional-buffer-deletion'
 | 
				
			||||||
  local file1 = base_file .. '1'
 | 
					  local file1 = base_file .. '1'
 | 
				
			||||||
  local file2 = base_file .. '2'
 | 
					  local file2 = base_file .. '2'
 | 
				
			||||||
@@ -227,6 +227,12 @@ describe('buffer deletion', function()
 | 
				
			|||||||
    command('edit ' .. file3)
 | 
					    command('edit ' .. file3)
 | 
				
			||||||
  end)
 | 
					  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()
 | 
					  it('deletes jump list entries when the current buffer is deleted', function()
 | 
				
			||||||
    command('edit ' .. file1)
 | 
					    command('edit ' .. file1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -319,6 +325,44 @@ describe('buffer deletion', function()
 | 
				
			|||||||
  end)
 | 
					  end)
 | 
				
			||||||
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()
 | 
					describe('jumpoptions=view', function()
 | 
				
			||||||
  local file1 = 'Xtestfile-functional-editor-jumps'
 | 
					  local file1 = 'Xtestfile-functional-editor-jumps'
 | 
				
			||||||
  local file2 = 'Xtestfile-functional-editor-jumps-2'
 | 
					  local file2 = 'Xtestfile-functional-editor-jumps-2'
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -13,6 +13,7 @@ if exists('s:did_load')
 | 
				
			|||||||
  set laststatus=1
 | 
					  set laststatus=1
 | 
				
			||||||
  set listchars=eol:$
 | 
					  set listchars=eol:$
 | 
				
			||||||
  set joinspaces
 | 
					  set joinspaces
 | 
				
			||||||
 | 
					  set jumpoptions=
 | 
				
			||||||
  set mousemodel=extend
 | 
					  set mousemodel=extend
 | 
				
			||||||
  set nohidden nosmarttab noautoindent noautoread noruler noshowcmd
 | 
					  set nohidden nosmarttab noautoindent noautoread noruler noshowcmd
 | 
				
			||||||
  set nohlsearch noincsearch
 | 
					  set nohlsearch noincsearch
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user