From e1a28e52b2b9391889826cbfaec473d8b615e22f Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Thu, 3 Sep 2026 11:32:20 -0400 Subject: [PATCH] fix(editor): shada "force" load clears v:oldfiles #41662 Problem: `shada_read()` rebuilds `v:oldfiles` for any "forced" read, which first *clears* the list. For an in-memory caller such as multicursor, the list is never repopulated. So `v:oldfiles` is empty after `Q` + edit. Solution: Rebuild `v:oldfiles` only when `kShaDaGetOldfiles` is requested. `shada_read_everything()` (`:rshada[!]`, startup) always requests it. old bug from 411a06c8b6e9 --- src/nvim/shada.c | 4 ++-- test/functional/api/vim_spec.lua | 5 +++++ test/functional/editor/mcursor_spec.lua | 4 ++++ 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/nvim/shada.c b/src/nvim/shada.c index e191180dae..115bb9f920 100644 --- a/src/nvim/shada.c +++ b/src/nvim/shada.c @@ -907,8 +907,8 @@ static void shada_read(FileDescriptor *const sd_reader, const int flags) { list_T *oldfiles_list = get_vim_var_list(VV_OLDFILES); const bool force = flags & kShaDaForceit; - const bool get_old_files = (flags & (kShaDaGetOldfiles | kShaDaForceit) - && (force || tv_list_len(oldfiles_list) == 0)); + const bool get_old_files = (flags & kShaDaGetOldfiles) + && (force || tv_list_len(oldfiles_list) == 0); const bool want_marks = flags & kShaDaWantMarks; const bool no_opt = flags & kShaDaNoOpt; const unsigned srni_flags = diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua index 3e694548e9..ca5a7baeaf 100644 --- a/test/functional/api/vim_spec.lua +++ b/test/functional/api/vim_spec.lua @@ -2892,6 +2892,11 @@ describe('API', function() eq(1, eval('g:one')) eq('', eval('&shada')) eq(0, eval("get(g:, 'optionset', 0)")) + + -- Does not touch v:oldfiles (only ":rshada!" rebuilds it). + command('let v:oldfiles = ["/a", "/b"]') + api.nvim_load_context(ctx) + eq({ '/a', '/b' }, eval('v:oldfiles')) end) it('errors when context dict is invalid', function() diff --git a/test/functional/editor/mcursor_spec.lua b/test/functional/editor/mcursor_spec.lua index 6f7758711c..7e5b4a1fc8 100644 --- a/test/functional/editor/mcursor_spec.lua +++ b/test/functional/editor/mcursor_spec.lua @@ -1133,6 +1133,7 @@ describe('multicursor', function() end) it('typed text live-mirrors at cursors', function() + command('let v:oldfiles = ["/a", "/b"] | let @a = "reg"') local screen = Screen.new(30, 6) cursors({ 'aaa', 'bbb', 'ccc' }) -- Still in insert mode (no yet): the text already cascaded; each cursor displays @@ -1155,6 +1156,9 @@ describe('multicursor', function() {1:~ }|*2 | ]]) + -- Cascade context-management should not clobber v:oldfiles. + eq({ '/a', '/b' }, api.nvim_get_vvar('oldfiles')) + -- Entering insert mode ("a") displays each cursor at its insertion-point immediately. feed('a') screen:expect([[