mirror of
https://github.com/neovim/neovim.git
synced 2026-08-25 16:41:52 +00:00
feat(cwd): support explicit chdir (:bcd/:tcd/…) in temp context
Problem:
- Explicit `:bcd` (etc.) persists from `nvim_buf_call()` but not from an
autocmd handler targeting a hidden buf (`LspAttach`, `TermRequest`, …),
which needs a `vim.schedule()` workaround.
- `vim._with()` is supposed to work as a "sandbox", discarding
side-effects, but it leaks CWD changes: `:lcd` from a `win` context,
any chdir from a visible-buffer context.
Solution:
- Explicit :cd/:tcd/:bcd during a temp context persists by default.
- "Ambient" directory changes ('autochdir', existing win-local CWD,
etc.) are still undone, as before.
- Add `kCtxKeepDirs`: snapshot/restore the target's full CWD state
(w/b/tp-local, global, cwd). Used by `vim._with()` and `'inccommand'`,
which must not leak state.
This commit is contained in:
@@ -344,6 +344,32 @@ describe('vim._with', function()
|
||||
]])
|
||||
eq(true, out)
|
||||
end)
|
||||
|
||||
it('restores CWD state', function()
|
||||
local out = exec_lua [[
|
||||
local other_buf, cur_buf = setup_buffers()
|
||||
local cwd = fn.getcwd()
|
||||
local dir = vim.fs.joinpath(cwd, 'test')
|
||||
-- ":bcd" on the target buffer is discarded: hidden target, (nested) visible target, and
|
||||
-- when the callback errors.
|
||||
vim._with({ buf = other_buf }, function()
|
||||
vim.cmd.bcd(dir)
|
||||
vim._with({ buf = cur_buf }, function()
|
||||
vim.cmd.bcd(dir)
|
||||
end)
|
||||
end)
|
||||
pcall(vim._with, { buf = other_buf }, function()
|
||||
vim.cmd.bcd(dir)
|
||||
error('oops')
|
||||
end)
|
||||
return {
|
||||
fn.haslocaldir(-1, -1, other_buf),
|
||||
fn.haslocaldir(-1, -1, cur_buf),
|
||||
fn.getcwd() == cwd,
|
||||
}
|
||||
]]
|
||||
eq({ 0, 0, true }, out)
|
||||
end)
|
||||
end)
|
||||
|
||||
describe('`cwd` context', function()
|
||||
@@ -381,6 +407,19 @@ describe('vim._with', function()
|
||||
]]
|
||||
eq({ true, true, true, true }, out)
|
||||
end)
|
||||
|
||||
it('does not modify global CWD', function()
|
||||
local out = exec_lua [[
|
||||
local other_buf, _ = setup_buffers()
|
||||
local cwd = fn.getcwd()
|
||||
-- Activate a window-local dir, so that the global dir must be remembered.
|
||||
vim.cmd.lcd(vim.fs.joinpath(cwd, 'test'))
|
||||
local lcd_cwd = fn.getcwd() -- Not necessarily `cwd .. '/test'`: symlinks are resolved.
|
||||
vim._with({ buf = other_buf, cwd = vim.fs.joinpath(cwd, 'src') }, function() end)
|
||||
return { fn.getcwd() == lcd_cwd, fn.getcwd(-1, -1) == cwd }
|
||||
]]
|
||||
eq({ true, true }, out)
|
||||
end)
|
||||
end)
|
||||
|
||||
describe('`emsg_silent` context', function()
|
||||
@@ -1305,6 +1344,19 @@ describe('vim._with', function()
|
||||
exec_lua('vim._with({ win = ... }, function() vim.cmd.wincmd "J" end)', t2_move_win)
|
||||
eq({ 'col', { { 'leaf', t2_other_win }, { 'leaf', t2_move_win } } }, fn.winlayout(2))
|
||||
end)
|
||||
|
||||
it('restores CWD state', function()
|
||||
local out = exec_lua [[
|
||||
local other_win, cur_win = setup_windows()
|
||||
local cwd = fn.getcwd()
|
||||
-- ":lcd" on the target window is discarded.
|
||||
vim._with({ win = other_win }, function()
|
||||
vim.cmd.lcd(vim.fs.joinpath(cwd, 'test'))
|
||||
end)
|
||||
return { fn.haslocaldir(fn.win_id2win(other_win)), fn.getcwd() == cwd }
|
||||
]]
|
||||
eq({ 0, true }, out)
|
||||
end)
|
||||
end)
|
||||
|
||||
describe('`wo` context', function()
|
||||
|
||||
Reference in New Issue
Block a user