From 71455173b482b9787340505151995da9e2b8f38f Mon Sep 17 00:00:00 2001 From: Andre Toerien Date: Sat, 19 Apr 2025 14:47:44 +0200 Subject: [PATCH 1/4] feat(shada): don't store jumplist if '0 in 'shada' --- runtime/doc/options.txt | 4 ++-- runtime/lua/vim/_meta/options.lua | 4 ++-- src/nvim/options.lua | 4 ++-- src/nvim/shada.c | 6 ++++-- test/functional/shada/marks_spec.lua | 31 ++++++++++++++++++++++++++++ 5 files changed, 41 insertions(+), 8 deletions(-) diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index 6779c867ce..10f66109ae 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -5187,8 +5187,8 @@ A jump table for the options with a short description can be found at |Q_op|. ' Maximum number of previously edited files for which the marks are remembered. This parameter must always be included when 'shada' is non-empty. - Including this item also means that the |jumplist| and the - |changelist| are stored in the shada file. + If non-zero, then the |jumplist| and the |changelist| are also + stored in the shada file. *shada-/* / Maximum number of items in the search pattern history to be saved. If non-zero, then the previous search and substitute diff --git a/runtime/lua/vim/_meta/options.lua b/runtime/lua/vim/_meta/options.lua index 1e0e40a3a3..25fbaae5a3 100644 --- a/runtime/lua/vim/_meta/options.lua +++ b/runtime/lua/vim/_meta/options.lua @@ -5441,8 +5441,8 @@ vim.go.ssop = vim.go.sessionoptions --- ' Maximum number of previously edited files for which the marks --- are remembered. This parameter must always be included when --- 'shada' is non-empty. ---- Including this item also means that the `jumplist` and the ---- `changelist` are stored in the shada file. +--- If non-zero, then the `jumplist` and the `changelist` are also +--- stored in the shada file. --- *shada-/* --- / Maximum number of items in the search pattern history to be --- saved. If non-zero, then the previous search and substitute diff --git a/src/nvim/options.lua b/src/nvim/options.lua index 624e4166a9..fdbd361bc8 100644 --- a/src/nvim/options.lua +++ b/src/nvim/options.lua @@ -7296,8 +7296,8 @@ local options = { ' Maximum number of previously edited files for which the marks are remembered. This parameter must always be included when 'shada' is non-empty. - Including this item also means that the |jumplist| and the - |changelist| are stored in the shada file. + If non-zero, then the |jumplist| and the |changelist| are also + stored in the shada file. *shada-/* / Maximum number of items in the search pattern history to be saved. If non-zero, then the previous search and substitute diff --git a/src/nvim/shada.c b/src/nvim/shada.c index 8496df73ad..66cdf4911a 100644 --- a/src/nvim/shada.c +++ b/src/nvim/shada.c @@ -2396,8 +2396,10 @@ static ShaDaWriteResult shada_write(FileDescriptor *const sd_writer, } while (var_iter != NULL); } - // Initialize jump list - wms->jumps_size = shada_init_jumps(wms->jumps, &removable_bufs); + if (num_marked_files > 0) { // Skip if '0 in 'shada' + // Initialize jump list + wms->jumps_size = shada_init_jumps(wms->jumps, &removable_bufs); + } const bool search_highlighted = !(no_hlsearch || find_shada_parameter('h') != NULL); diff --git a/test/functional/shada/marks_spec.lua b/test/functional/shada/marks_spec.lua index 837978dee1..528fa3a008 100644 --- a/test/functional/shada/marks_spec.lua +++ b/test/functional/shada/marks_spec.lua @@ -152,6 +152,37 @@ describe('ShaDa support code', function() eq(saved, exec_capture('jumps')) end) + it("does not dump jumplist if `'0` in shada", function() + local empty_jumps = exec_capture('jumps') + nvim_command("set shada='0") + nvim_command('edit ' .. testfilename_2) + nvim_command('normal! G') + nvim_command('normal! gg') + nvim_command('edit ' .. testfilename) + nvim_command('normal! G') + nvim_command('normal! gg') + nvim_command('enew') + nvim_command('normal! gg') + expect_exit(nvim_command, 'qall') + reset() + eq(empty_jumps, exec_capture('jumps')) + end) + + it("does read back jumplist even with `'0` in shada", function() + nvim_command('edit ' .. testfilename_2) + nvim_command('normal! G') + nvim_command('normal! gg') + nvim_command('edit ' .. testfilename) + nvim_command('normal! G') + nvim_command('normal! gg') + nvim_command('enew') + nvim_command('normal! gg') + local saved = exec_capture('jumps') + expect_exit(nvim_command, 'qall') + reset("set shada='0") + eq(saved, exec_capture('jumps')) + end) + it('when dumping jump list also dumps current position', function() nvim_command('edit ' .. testfilename) nvim_command('normal! G') From a6591950f6bc658de0889a6d8a8a088caec6246b Mon Sep 17 00:00:00 2001 From: Andre Toerien Date: Sat, 19 Apr 2025 14:54:02 +0200 Subject: [PATCH 2/4] fix(shada): don't store search and sub patterns if /0 in 'shada' --- src/nvim/shada.c | 22 +++++++++---------- test/functional/shada/history_spec.lua | 29 ++++++++++++++++++++++++-- 2 files changed, 38 insertions(+), 13 deletions(-) diff --git a/src/nvim/shada.c b/src/nvim/shada.c index 66cdf4911a..784a27d294 100644 --- a/src/nvim/shada.c +++ b/src/nvim/shada.c @@ -2401,20 +2401,20 @@ static ShaDaWriteResult shada_write(FileDescriptor *const sd_writer, wms->jumps_size = shada_init_jumps(wms->jumps, &removable_bufs); } - const bool search_highlighted = !(no_hlsearch - || find_shada_parameter('h') != NULL); - const bool search_last_used = search_was_last_used(); + if (dump_one_history[HIST_SEARCH] > 0) { // Skip if /0 in 'shada' + const bool search_highlighted = !(no_hlsearch + || find_shada_parameter('h') != NULL); + const bool search_last_used = search_was_last_used(); - // Initialize search pattern - add_search_pattern(&wms->search_pattern, &get_search_pattern, false, - search_last_used, search_highlighted); + // Initialize search pattern + add_search_pattern(&wms->search_pattern, &get_search_pattern, false, + search_last_used, search_highlighted); - // Initialize substitute search pattern - add_search_pattern(&wms->sub_search_pattern, &get_substitute_pattern, true, - search_last_used, search_highlighted); + // Initialize substitute search pattern + add_search_pattern(&wms->sub_search_pattern, &get_substitute_pattern, true, + search_last_used, search_highlighted); - // Initialize substitute replacement string - { + // Initialize substitute replacement string SubReplacementString sub; sub_get_replacement(&sub); wms->replacement = (PossiblyFreedShadaEntry) { diff --git a/test/functional/shada/history_spec.lua b/test/functional/shada/history_spec.lua index 39a5f8df7e..1eccff5162 100644 --- a/test/functional/shada/history_spec.lua +++ b/test/functional/shada/history_spec.lua @@ -6,6 +6,7 @@ local t_shada = require('test.functional.shada.testutil') local nvim_command, fn, api, nvim_feed, eq = n.command, n.fn, n.api, n.feed, t.eq local assert_alive = n.assert_alive local expect_exit = n.expect_exit +local pcall_err = t.pcall_err local reset, clear = t_shada.reset, t_shada.clear @@ -104,6 +105,16 @@ describe('ShaDa support code', function() eq('c', fn.histget('>', -1)) end) + it('does not dump last search pattern if /0 in shada', function() + nvim_command('set shada+=/0') + nvim_command('silent! /test/') + expect_exit(nvim_command, 'qall!') + reset() + nvim_command('language C') + local err = pcall_err(n.exec_capture, 'normal! n') + eq('nvim_exec2(), line 1: Vim(normal):E35: No previous regular expression', err) + end) + it('dumps and loads last search pattern with offset', function() api.nvim_set_option_value('wrapscan', false, {}) fn.setline('.', { 'foo', 'bar--' }) @@ -182,6 +193,22 @@ describe('ShaDa support code', function() eq('goo', fn.getline(1)) end) + it('does not dump last substitute pattern or replacement string if /0 in shada', function() + nvim_command('set shada+=/0') + fn.setline('.', { 'foo', 'bar' }) + nvim_command('%s/f/g/g') + eq('goo', fn.getline(1)) + expect_exit(nvim_command, 'qall!') + reset() + fn.setline('.', { 'foo', 'bar' }) + nvim_command('language C') + local err = pcall_err(nvim_command, '&') + eq('Vim(&):E33: No previous substitute regular expression', err) + nvim_feed('/f\n') + err = pcall_err(nvim_command, '~&') + eq('Vim(~):E33: No previous substitute regular expression', err) + end) + it('dumps and loads history with UTF-8 characters', function() reset() nvim_feed(':echo "«"\n') @@ -210,7 +237,6 @@ describe('ShaDa support code', function() it('dumps and loads search pattern with UTF-8 characters', function() nvim_command('silent! /«/') - nvim_command('set shada+=/0') expect_exit(nvim_command, 'qall!') reset() fn.setline('.', { '\171«' }) @@ -222,7 +248,6 @@ describe('ShaDa support code', function() it('dumps and loads search pattern with 8-bit single-byte', function() -- \171 is U+00AB LEFT-POINTING DOUBLE ANGLE QUOTATION MARK in latin1 nvim_command('silent! /\171/') - nvim_command('set shada+=/0') expect_exit(nvim_command, 'qall!') reset() fn.setline('.', { '\171«' }) From 1f503ac7c0df308d97e265b4582aa0f9c76b13b6 Mon Sep 17 00:00:00 2001 From: Andre Toerien Date: Sat, 19 Apr 2025 14:55:39 +0200 Subject: [PATCH 3/4] fix(shada): don't store empty replacement string --- src/nvim/shada.c | 26 ++++++++++++++------------ test/functional/shada/history_spec.lua | 24 ++++++++++++++++++------ 2 files changed, 32 insertions(+), 18 deletions(-) diff --git a/src/nvim/shada.c b/src/nvim/shada.c index 784a27d294..71ce4ce9ee 100644 --- a/src/nvim/shada.c +++ b/src/nvim/shada.c @@ -2417,19 +2417,21 @@ static ShaDaWriteResult shada_write(FileDescriptor *const sd_writer, // Initialize substitute replacement string SubReplacementString sub; sub_get_replacement(&sub); - wms->replacement = (PossiblyFreedShadaEntry) { - .can_free_entry = false, - .data = { - .type = kSDItemSubString, - .timestamp = sub.timestamp, + if (sub.sub != NULL) { // Don't store empty replacement string + wms->replacement = (PossiblyFreedShadaEntry) { + .can_free_entry = false, .data = { - .sub_string = { - .sub = sub.sub, - } - }, - .additional_data = sub.additional_data, - } - }; + .type = kSDItemSubString, + .timestamp = sub.timestamp, + .data = { + .sub_string = { + .sub = sub.sub, + } + }, + .additional_data = sub.additional_data, + } + }; + } } // Initialize global marks diff --git a/test/functional/shada/history_spec.lua b/test/functional/shada/history_spec.lua index 1eccff5162..84d5415af8 100644 --- a/test/functional/shada/history_spec.lua +++ b/test/functional/shada/history_spec.lua @@ -239,9 +239,10 @@ describe('ShaDa support code', function() nvim_command('silent! /«/') expect_exit(nvim_command, 'qall!') reset() - fn.setline('.', { '\171«' }) - nvim_command('~&') - eq('\171', fn.getline('.')) + fn.setline('.', { 'foo', '\171«' }) + nvim_feed('gg0n') + eq({ 0, 2, 2, 0 }, fn.getpos('.')) + eq('\171«', fn.getline('.')) eq('', fn.histget('/', -1)) end) @@ -250,9 +251,10 @@ describe('ShaDa support code', function() nvim_command('silent! /\171/') expect_exit(nvim_command, 'qall!') reset() - fn.setline('.', { '\171«' }) - nvim_command('~&') - eq('«', fn.getline('.')) + fn.setline('.', { 'foo', '\171«' }) + nvim_feed('gg0n') + eq({ 0, 2, 1, 0 }, fn.getpos('.')) + eq('\171«', fn.getline('.')) eq('', fn.histget('/', -1)) end) @@ -277,4 +279,14 @@ describe('ShaDa support code', function() nvim_command('wshada') assert_alive() end) + + it('does not dump empty replacement string', function() + expect_exit(nvim_command, 'qall!') + reset() + fn.setline('.', { 'foo', 'bar' }) + nvim_command('language C') + nvim_feed('/f\n') + local err = pcall_err(nvim_command, '~&') + eq('Vim(~):E33: No previous substitute regular expression', err) + end) end) From 36dbd2686f450e5bf5ff7ac7a0cd42ef104e022d Mon Sep 17 00:00:00 2001 From: Andre Toerien Date: Sat, 19 Apr 2025 14:57:13 +0200 Subject: [PATCH 4/4] fix(shada): don't add '0' mark if f0 in 'shada' --- src/nvim/shada.c | 6 +- test/functional/shada/marks_spec.lua | 94 ++++++++++++++++++++++++++-- 2 files changed, 93 insertions(+), 7 deletions(-) diff --git a/src/nvim/shada.c b/src/nvim/shada.c index 71ce4ce9ee..b70ee62674 100644 --- a/src/nvim/shada.c +++ b/src/nvim/shada.c @@ -2559,9 +2559,9 @@ static ShaDaWriteResult shada_write(FileDescriptor *const sd_writer, } } - // Update numbered marks: '0' should be replaced with the current position, - // '9' should be removed and all other marks shifted. - if (!ignore_buf(curbuf, &removable_bufs) && curwin->w_cursor.lnum != 0) { + // Update numbered marks: replace '0 mark with the current position, + // remove '9 and shift all other marks. Skip if f0 in 'shada'. + if (dump_global_marks && !ignore_buf(curbuf, &removable_bufs) && curwin->w_cursor.lnum != 0) { replace_numbered_mark(wms, 0, (PossiblyFreedShadaEntry) { .can_free_entry = false, .data = { diff --git a/test/functional/shada/marks_spec.lua b/test/functional/shada/marks_spec.lua index 528fa3a008..8463a2d675 100644 --- a/test/functional/shada/marks_spec.lua +++ b/test/functional/shada/marks_spec.lua @@ -51,29 +51,115 @@ describe('ShaDa support code', function() eq(2, nvim_current_line()) end) - it('does not dump global mark with `f0` in shada', function() + it('can dump and read back numbered marks', function() + local function move(cmd) + feed(cmd) + nvim_command('wshada') + end + nvim_command('edit ' .. testfilename) + move('l') + move('l') + move('j') + move('l') + move('l') + nvim_command('edit ' .. testfilename_2) + move('l') + move('l') + move('j') + move('l') + move('l') + -- we have now populated marks 0 through 9 + nvim_command('edit ' .. testfilename) + feed('gg0') + -- during shada save on exit, mark 0 will become the current position, + -- 9 will be removed, and all other marks shifted + expect_exit(nvim_command, 'qall') + reset() + nvim_command('edit ' .. testfilename) + nvim_command('edit ' .. testfilename_2) + local marklist = fn.getmarklist() + for _, mark in ipairs(marklist) do + mark.file = fn.fnamemodify(mark.file, ':t') + end + eq({ + { + file = testfilename, + mark = "'0", + pos = { 1, 1, 1, 0 }, + }, + { + file = testfilename_2, + mark = "'1", + pos = { 2, 2, 5, 0 }, + }, + { + file = testfilename_2, + mark = "'2", + pos = { 2, 2, 4, 0 }, + }, + { + file = testfilename_2, + mark = "'3", + pos = { 2, 2, 3, 0 }, + }, + { + file = testfilename_2, + mark = "'4", + pos = { 2, 1, 3, 0 }, + }, + { + file = testfilename_2, + mark = "'5", + pos = { 2, 1, 2, 0 }, + }, + { + file = testfilename, + mark = "'6", + pos = { 1, 2, 5, 0 }, + }, + { + file = testfilename, + mark = "'7", + pos = { 1, 2, 4, 0 }, + }, + { + file = testfilename, + mark = "'8", + pos = { 1, 2, 3, 0 }, + }, + { + file = testfilename, + mark = "'9", + pos = { 1, 1, 3, 0 }, + }, + }, marklist) + end) + + it('does not dump global or numbered marks with `f0` in shada', function() nvim_command('set shada+=f0') nvim_command('edit ' .. testfilename) nvim_command('mark A') nvim_command('2') - nvim_command('kB') nvim_command('wshada') reset() nvim_command('language C') eq('Vim(normal):E20: Mark not set', exc_exec('normal! `A')) + eq('Vim(normal):E20: Mark not set', exc_exec('normal! `0')) end) - it("does read back global mark even with `'0` and `f0` in shada", function() + it("restores global and numbered marks even with `'0` and `f0` in shada", function() nvim_command('edit ' .. testfilename) nvim_command('mark A') nvim_command('2') - nvim_command('kB') nvim_command('wshada') reset("set shada='0,f0") nvim_command('language C') nvim_command('normal! `A') eq(testfilename, fn.fnamemodify(api.nvim_buf_get_name(0), ':t')) eq(1, nvim_current_line()) + nvim_command('normal! `0') + eq(testfilename, fn.fnamemodify(api.nvim_buf_get_name(0), ':t')) + eq(2, nvim_current_line()) end) it('is able to dump and read back local mark', function()