Compare commits

...

2 Commits

Author SHA1 Message Date
bfredl
ef0e845519
Merge 86ef062c8c into 7121983c45 2024-12-18 14:40:43 +00:00
bfredl
86ef062c8c fix(wininfo): when freeing windows, free the lowest priority wininfo
On master (and also before #31539) deleting a window could cause the
used wininfo for a buffer to change. This is due to always removing the
previous NULL wininfo when deleting a window, even if that wininfo had
higher priority than the the deleted window's own wininfo.

Instead delete the wininfo with lowest priority. This retains the memory
saving efect while not affecting the effective value of window options
and so on.
2024-12-17 10:59:27 +01:00
2 changed files with 49 additions and 4 deletions

View File

@ -5230,11 +5230,13 @@ void win_free(win_T *wp, tabpage_T *tp)
// freed memory is re-used for another window.
FOR_ALL_BUFFERS(buf) {
WinInfo *wip_wp = NULL;
size_t pos_wip = kv_size(buf->b_wininfo);
size_t pos_null = kv_size(buf->b_wininfo);
for (size_t i = 0; i < kv_size(buf->b_wininfo); i++) {
WinInfo *wip = kv_A(buf->b_wininfo, i);
if (wip->wi_win == wp) {
wip_wp = wip;
pos_wip = i;
} else if (wip->wi_win == NULL) {
pos_null = i;
}
@ -5242,11 +5244,12 @@ void win_free(win_T *wp, tabpage_T *tp)
if (wip_wp) {
wip_wp->wi_win = NULL;
// If there already is an entry with "wi_win" set to NULL it
// must be removed, it would never be used.
// If there already is an entry with "wi_win" set to NULL, only
// the first entry with NULL will ever be used, delete the other one.
if (pos_null < kv_size(buf->b_wininfo)) {
free_wininfo(kv_A(buf->b_wininfo, pos_null), buf);
kv_shift(buf->b_wininfo, pos_null, 1);
size_t pos_delete = MAX(pos_null, pos_wip);
free_wininfo(kv_A(buf->b_wininfo, pos_delete), buf);
kv_shift(buf->b_wininfo, pos_delete, 1);
}
}
}

View File

@ -506,6 +506,48 @@ describe('API/win', function()
assert_alive()
end)
describe('after deleting', function()
local buf, win0, win1, win2
before_each(function()
win0 = api.nvim_get_current_win()
command('new')
buf = api.nvim_get_current_buf()
win1 = api.nvim_get_current_win()
command('set numberwidth=10')
command('split')
win2 = api.nvim_get_current_win()
command('set numberwidth=15')
command('enew')
api.nvim_set_current_win(win1)
command('normal ix')
command('enew')
api.nvim_set_current_win(win0)
eq(4, api.nvim_get_option_value('numberwidth', {}))
end)
-- at this point buffer `buf` is current in no windows. deletion shouldn't affect its defaults
it('0 windows', function()
api.nvim_set_current_buf(buf)
eq(10, api.nvim_get_option_value('numberwidth', {}))
end)
it('1 window', function()
api.nvim_win_close(win1, false)
api.nvim_set_current_buf(buf)
eq(10, api.nvim_get_option_value('numberwidth', {}))
end)
it('2 windows', function()
api.nvim_win_close(win1, false)
api.nvim_win_close(win2, false)
api.nvim_set_current_buf(buf)
eq(10, api.nvim_get_option_value('numberwidth', {}))
end)
end)
it('returns values for unset local options', function()
eq(-1, api.nvim_get_option_value('scrolloff', { win = 0, scope = 'local' }))
end)