Merge #33542 shada improvements

* feat(shada): don't store jumplist if '0 in 'shada'
* fix(shada): don't store search and sub patterns if /0 in 'shada'
* fix(shada): don't store empty replacement string
* fix(shada): don't add '0' mark if f0 in 'shada'
This commit is contained in:
Justin M. Keyes
2025-04-27 16:15:30 -07:00
committed by GitHub
6 changed files with 204 additions and 46 deletions

View File

@@ -5193,8 +5193,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 ' Maximum number of previously edited files for which the marks
are remembered. This parameter must always be included when are remembered. This parameter must always be included when
'shada' is non-empty. 'shada' is non-empty.
Including this item also means that the |jumplist| and the If non-zero, then the |jumplist| and the |changelist| are also
|changelist| are stored in the shada file. stored in the shada file.
*shada-/* *shada-/*
/ Maximum number of items in the search pattern history to be / Maximum number of items in the search pattern history to be
saved. If non-zero, then the previous search and substitute saved. If non-zero, then the previous search and substitute

View File

@@ -5450,8 +5450,8 @@ vim.go.ssop = vim.go.sessionoptions
--- ' Maximum number of previously edited files for which the marks --- ' Maximum number of previously edited files for which the marks
--- are remembered. This parameter must always be included when --- are remembered. This parameter must always be included when
--- 'shada' is non-empty. --- 'shada' is non-empty.
--- Including this item also means that the `jumplist` and the --- If non-zero, then the `jumplist` and the `changelist` are also
--- `changelist` are stored in the shada file. --- stored in the shada file.
--- *shada-/* --- *shada-/*
--- / Maximum number of items in the search pattern history to be --- / Maximum number of items in the search pattern history to be
--- saved. If non-zero, then the previous search and substitute --- saved. If non-zero, then the previous search and substitute

View File

@@ -7302,8 +7302,8 @@ local options = {
' Maximum number of previously edited files for which the marks ' Maximum number of previously edited files for which the marks
are remembered. This parameter must always be included when are remembered. This parameter must always be included when
'shada' is non-empty. 'shada' is non-empty.
Including this item also means that the |jumplist| and the If non-zero, then the |jumplist| and the |changelist| are also
|changelist| are stored in the shada file. stored in the shada file.
*shada-/* *shada-/*
/ Maximum number of items in the search pattern history to be / Maximum number of items in the search pattern history to be
saved. If non-zero, then the previous search and substitute saved. If non-zero, then the previous search and substitute

View File

@@ -2396,9 +2396,12 @@ static ShaDaWriteResult shada_write(FileDescriptor *const sd_writer,
} while (var_iter != NULL); } while (var_iter != NULL);
} }
if (num_marked_files > 0) { // Skip if '0 in 'shada'
// Initialize jump list // Initialize jump list
wms->jumps_size = shada_init_jumps(wms->jumps, &removable_bufs); wms->jumps_size = shada_init_jumps(wms->jumps, &removable_bufs);
}
if (dump_one_history[HIST_SEARCH] > 0) { // Skip if /0 in 'shada'
const bool search_highlighted = !(no_hlsearch const bool search_highlighted = !(no_hlsearch
|| find_shada_parameter('h') != NULL); || find_shada_parameter('h') != NULL);
const bool search_last_used = search_was_last_used(); const bool search_last_used = search_was_last_used();
@@ -2412,9 +2415,9 @@ static ShaDaWriteResult shada_write(FileDescriptor *const sd_writer,
search_last_used, search_highlighted); search_last_used, search_highlighted);
// Initialize substitute replacement string // Initialize substitute replacement string
{
SubReplacementString sub; SubReplacementString sub;
sub_get_replacement(&sub); sub_get_replacement(&sub);
if (sub.sub != NULL) { // Don't store empty replacement string
wms->replacement = (PossiblyFreedShadaEntry) { wms->replacement = (PossiblyFreedShadaEntry) {
.can_free_entry = false, .can_free_entry = false,
.data = { .data = {
@@ -2429,6 +2432,7 @@ static ShaDaWriteResult shada_write(FileDescriptor *const sd_writer,
} }
}; };
} }
}
// Initialize global marks // Initialize global marks
if (dump_global_marks) { if (dump_global_marks) {
@@ -2555,9 +2559,9 @@ static ShaDaWriteResult shada_write(FileDescriptor *const sd_writer,
} }
} }
// Update numbered marks: '0' should be replaced with the current position, // Update numbered marks: replace '0 mark with the current position,
// '9' should be removed and all other marks shifted. // remove '9 and shift all other marks. Skip if f0 in 'shada'.
if (!ignore_buf(curbuf, &removable_bufs) && curwin->w_cursor.lnum != 0) { if (dump_global_marks && !ignore_buf(curbuf, &removable_bufs) && curwin->w_cursor.lnum != 0) {
replace_numbered_mark(wms, 0, (PossiblyFreedShadaEntry) { replace_numbered_mark(wms, 0, (PossiblyFreedShadaEntry) {
.can_free_entry = false, .can_free_entry = false,
.data = { .data = {

View File

@@ -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 nvim_command, fn, api, nvim_feed, eq = n.command, n.fn, n.api, n.feed, t.eq
local assert_alive = n.assert_alive local assert_alive = n.assert_alive
local expect_exit = n.expect_exit local expect_exit = n.expect_exit
local pcall_err = t.pcall_err
local reset, clear = t_shada.reset, t_shada.clear local reset, clear = t_shada.reset, t_shada.clear
@@ -104,6 +105,16 @@ describe('ShaDa support code', function()
eq('c', fn.histget('>', -1)) eq('c', fn.histget('>', -1))
end) 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() it('dumps and loads last search pattern with offset', function()
api.nvim_set_option_value('wrapscan', false, {}) api.nvim_set_option_value('wrapscan', false, {})
fn.setline('.', { 'foo', 'bar--' }) fn.setline('.', { 'foo', 'bar--' })
@@ -182,6 +193,22 @@ describe('ShaDa support code', function()
eq('goo', fn.getline(1)) eq('goo', fn.getline(1))
end) 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() it('dumps and loads history with UTF-8 characters', function()
reset() reset()
nvim_feed(':echo "«"\n') nvim_feed(':echo "«"\n')
@@ -210,24 +237,24 @@ describe('ShaDa support code', function()
it('dumps and loads search pattern with UTF-8 characters', function() it('dumps and loads search pattern with UTF-8 characters', function()
nvim_command('silent! /«/') nvim_command('silent! /«/')
nvim_command('set shada+=/0')
expect_exit(nvim_command, 'qall!') expect_exit(nvim_command, 'qall!')
reset() reset()
fn.setline('.', { '\171«' }) fn.setline('.', { 'foo', '\171«' })
nvim_command('~&') nvim_feed('gg0n')
eq('\171', fn.getline('.')) eq({ 0, 2, 2, 0 }, fn.getpos('.'))
eq('\171«', fn.getline('.'))
eq('', fn.histget('/', -1)) eq('', fn.histget('/', -1))
end) end)
it('dumps and loads search pattern with 8-bit single-byte', 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 -- \171 is U+00AB LEFT-POINTING DOUBLE ANGLE QUOTATION MARK in latin1
nvim_command('silent! /\171/') nvim_command('silent! /\171/')
nvim_command('set shada+=/0')
expect_exit(nvim_command, 'qall!') expect_exit(nvim_command, 'qall!')
reset() reset()
fn.setline('.', { '\171«' }) fn.setline('.', { 'foo', '\171«' })
nvim_command('~&') nvim_feed('gg0n')
eq('«', fn.getline('.')) eq({ 0, 2, 1, 0 }, fn.getpos('.'))
eq('\171«', fn.getline('.'))
eq('', fn.histget('/', -1)) eq('', fn.histget('/', -1))
end) end)
@@ -252,4 +279,14 @@ describe('ShaDa support code', function()
nvim_command('wshada') nvim_command('wshada')
assert_alive() assert_alive()
end) 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) end)

View File

@@ -51,29 +51,115 @@ describe('ShaDa support code', function()
eq(2, nvim_current_line()) eq(2, nvim_current_line())
end) 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('set shada+=f0')
nvim_command('edit ' .. testfilename) nvim_command('edit ' .. testfilename)
nvim_command('mark A') nvim_command('mark A')
nvim_command('2') nvim_command('2')
nvim_command('kB')
nvim_command('wshada') nvim_command('wshada')
reset() reset()
nvim_command('language C') 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! `A'))
eq('Vim(normal):E20: Mark not set', exc_exec('normal! `0'))
end) 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('edit ' .. testfilename)
nvim_command('mark A') nvim_command('mark A')
nvim_command('2') nvim_command('2')
nvim_command('kB')
nvim_command('wshada') nvim_command('wshada')
reset("set shada='0,f0") reset("set shada='0,f0")
nvim_command('language C') nvim_command('language C')
nvim_command('normal! `A') nvim_command('normal! `A')
eq(testfilename, fn.fnamemodify(api.nvim_buf_get_name(0), ':t')) eq(testfilename, fn.fnamemodify(api.nvim_buf_get_name(0), ':t'))
eq(1, nvim_current_line()) 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) end)
it('is able to dump and read back local mark', function() it('is able to dump and read back local mark', function()
@@ -152,6 +238,37 @@ describe('ShaDa support code', function()
eq(saved, exec_capture('jumps')) eq(saved, exec_capture('jumps'))
end) 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() it('when dumping jump list also dumps current position', function()
nvim_command('edit ' .. testfilename) nvim_command('edit ' .. testfilename)
nvim_command('normal! G') nvim_command('normal! G')