mirror of
https://github.com/neovim/neovim.git
synced 2026-08-26 00:51:53 +00:00
fix(cwd): nvim_win_set_buf of :bcd buf, changes caller CWD #41329
Problem:
Setting a :bcd buffer into another window, modifies the caller's CWD.
local b = vim.api.nvim_create_buf(true, true)
vim.api.nvim_buf_call(b, function() vim.cmd.bcd('..') end)
vim.cmd('vsplit')
vim.api.nvim_win_set_buf(vim.fn.win_getid(2), b)
:echo haslocaldir(0) haslocaldir(-1,0) haslocaldir(-1,-1,0)
0 0 0
:echo getcwd() ==# getcwd(-1,-1)
0
Analysis:
`ctx_dirs_save` only saves CWD if it predicts the switch can change it.
But win_set_buf() replaces the target window's buffer *after* the
switch, which cannot be "predicted" from `ctx_dirs_save`.
Solution:
Always snapshot whenever the switch enters another window.
Skipping `os_dirname` was a micro-optimization.
This commit is contained in:
@@ -338,15 +338,8 @@ static void ctx_dirs_save(CtxSwitch *cs, win_T *wp, tabpage_T *tp, buf_T *buf)
|
||||
cs->cs_tp_localdir = tp->tp_localdir == NULL ? NULL : xstrdup(tp->tp_localdir);
|
||||
}
|
||||
|
||||
// Getting and setting directory can be slow on some systems, only do this when the current or
|
||||
// target window/tab have a local directory or 'acd' is set, or if kCtxKeepDirs was set.
|
||||
char cwd[MAXPATHL];
|
||||
if ((cs->cs_flags & kCtxKeepDirs)
|
||||
|| (curwin != wp
|
||||
&& (curwin->w_localdir != NULL || wp->w_localdir != NULL
|
||||
|| curbuf->b_localdir != NULL || wp->w_buffer->b_localdir != NULL
|
||||
|| (curtab != tp && (curtab->tp_localdir != NULL || tp->tp_localdir != NULL))
|
||||
|| p_acd))) {
|
||||
if ((cs->cs_flags & kCtxKeepDirs) || curwin != wp) {
|
||||
if (os_dirname(cwd, MAXPATHL) == OK) {
|
||||
cs->cs_cwd = xstrdup(cwd); // allocated on demand: keeps CtxSwitch small
|
||||
}
|
||||
@@ -392,17 +385,19 @@ static void ctx_dirs_restore(CtxSwitch *cs)
|
||||
}
|
||||
XFREE_CLEAR(cs->cs_globaldir);
|
||||
|
||||
// Restore the CWD itself.
|
||||
// Restore the CWD itself. After an explicit chdir, ctx_restore() re-derives it instead.
|
||||
if (cs->cs_apply_acd) {
|
||||
xfree(cs->cs_save_sfname);
|
||||
do_autochdir();
|
||||
} else if (cs->cs_cwd != NULL) {
|
||||
} else if (cs->cs_cwd != NULL && ((cs->cs_flags & kCtxKeepDirs) || !_ctx_did_chdir)) {
|
||||
os_chdir(cs->cs_cwd);
|
||||
if (cs->cs_save_sfname != NULL) {
|
||||
xfree(curbuf->b_sfname);
|
||||
curbuf->b_sfname = cs->cs_save_sfname;
|
||||
curbuf->b_fname = curbuf->b_sfname;
|
||||
}
|
||||
} else {
|
||||
xfree(cs->cs_save_sfname);
|
||||
}
|
||||
XFREE_CLEAR(cs->cs_cwd);
|
||||
}
|
||||
|
||||
@@ -603,6 +603,18 @@ describe('cd during temp context-switch', function()
|
||||
command('wincmd w')
|
||||
eq({ startdir, startdir }, { cwd(), cwd(-1, -1) })
|
||||
command('only')
|
||||
|
||||
-- Setting a :bcd buffer into another window, should not modify the caller's CWD.
|
||||
local bcdbuf = call('nvim_create_buf', true, true)
|
||||
n.exec_lua(function(b, d)
|
||||
vim.api.nvim_buf_call(b, function()
|
||||
vim.cmd.bcd(d)
|
||||
end)
|
||||
end, bcdbuf, bufdir)
|
||||
command('split')
|
||||
call('nvim_win_set_buf', call('win_getid', 2), bcdbuf)
|
||||
eq({ bufdir, startdir, startdir }, { cwd(-1, -1, bcdbuf), cwd(), cwd(-1, -1) })
|
||||
command('only')
|
||||
end)
|
||||
end)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user