mirror of
https://github.com/neovim/neovim.git
synced 2026-08-26 17:11:48 +00:00
fix(:bcd): do not "inherit" buffer-local dir
Problem: Buffer-local CWD (:bcd) is "sticky", similar to window-local CWD (:lcd). But this contradicts one of its main benefits: per-buffer "project root" for LSP, OSC7. Other problems: - A buffer created with :edit/:enew/:new silently inherits b_localdir (and b_prevdir) from the previous buffer. - curbuf_reusable() refuses to recycle a scratch buffer that has `b_localdir`. - After :new/:vnew/:tabnew the CWD sticks to previous buffer's `b_localdir` even though the new curbuf has none, so :new is not equivalent to ":split | enew", and getcwd() disagrees with haslocaldir(). - Requires "which buffer spawned this buffer" semantics that no other buffer-local state has. Solution: Drop sticky/inherit behavior of buffer-local CWD (:bcd). - do_ecmd: always apply the new curbuf's dir (`fix_current_dir`), like `do_autochdir` already does. :tabnew from a :bcd buffer now reverts to global CWD (and fires DirChanged), same as :tabnew from a :lcd window. - curbuf_reusable(): recycling a scratch buffer frees its b_localdir. To get sticky/inherit behavior of CWD, use `:lcd`.
This commit is contained in:
@@ -6,7 +6,6 @@ local describe, it, before_each, setup, teardown =
|
||||
local clear = n.clear
|
||||
local command = n.command
|
||||
local eq = t.eq
|
||||
local pcall_err = t.pcall_err
|
||||
local eval = n.eval
|
||||
local request = n.request
|
||||
local is_os = t.is_os
|
||||
@@ -162,7 +161,7 @@ describe('autocmd DirChanged and DirChangedPre', function()
|
||||
eq('E344:', string.match(err3, 'E%d*:'))
|
||||
end)
|
||||
|
||||
it("are triggered by 'autochdir'", function()
|
||||
it("triggered by 'autochdir'", function()
|
||||
command('set autochdir')
|
||||
|
||||
command('split ' .. dirs[1] .. '/foo')
|
||||
@@ -181,7 +180,7 @@ describe('autocmd DirChanged and DirChangedPre', function()
|
||||
eq(2, eval('g:cdcount'))
|
||||
end)
|
||||
|
||||
it('do not trigger if directory has not changed', function()
|
||||
it('not triggered if directory has not changed', function()
|
||||
command('lcd ' .. dirs[1])
|
||||
eq({ directory = dirs[1], scope = 'window', changed_window = false }, eval('g:evpre'))
|
||||
eq({ cwd = dirs[1], scope = 'window', changed_window = false }, eval('g:ev'))
|
||||
@@ -299,7 +298,7 @@ describe('autocmd DirChanged and DirChangedPre', function()
|
||||
end
|
||||
end)
|
||||
|
||||
it('are triggered by switching to win/tab with different CWD #6054', function()
|
||||
it('triggered by switching to win/tab with different CWD #6054', function()
|
||||
command('lcd ' .. dirs[3]) -- window 3
|
||||
command('split ' .. dirs[2] .. '/foo') -- window 2
|
||||
command('lcd ' .. dirs[2])
|
||||
@@ -376,7 +375,7 @@ describe('autocmd DirChanged and DirChangedPre', function()
|
||||
end
|
||||
end)
|
||||
|
||||
it('are triggered by switching to buf/tab with different CWD', function()
|
||||
it('triggered by switching to buf/tab with different CWD', function()
|
||||
local files = {
|
||||
dirs[1] .. '/file',
|
||||
dirs[2] .. '/file',
|
||||
@@ -400,9 +399,10 @@ describe('autocmd DirChanged and DirChangedPre', function()
|
||||
|
||||
eq(5, eval('g:cdprecount'))
|
||||
eq(5, eval('g:cdcount'))
|
||||
command('tabnew') -- tab 2 (tab-local CWD)
|
||||
eq(5, eval('g:cdprecount')) -- same CWD, no DirChangedPre event
|
||||
eq(5, eval('g:cdcount')) -- same CWD, no DirChanged event
|
||||
command('tabnew') -- tab 2: its new empty buffer has no local CWD, reverts to global
|
||||
eq({ cwd = curdir, scope = 'global', changed_window = false }, eval('g:ev'))
|
||||
eq(6, eval('g:cdprecount'))
|
||||
eq(6, eval('g:cdcount'))
|
||||
command('tcd ' .. dirs[2])
|
||||
command('tabnext') -- tab 1 (no tab-local CWD)
|
||||
eq({ directory = dirs[3], scope = 'buffer', changed_window = true }, eval('g:evpre'))
|
||||
@@ -414,55 +414,55 @@ describe('autocmd DirChanged and DirChangedPre', function()
|
||||
eq({ cwd = dirs[2], scope = 'tabpage', changed_window = true }, eval('g:ev'))
|
||||
eq('tabpage', eval('g:amatchpre'))
|
||||
eq('tabpage', eval('g:amatch'))
|
||||
eq(8, eval('g:cdprecount'))
|
||||
eq(8, eval('g:cdcount'))
|
||||
eq(9, eval('g:cdprecount'))
|
||||
eq(9, eval('g:cdcount'))
|
||||
|
||||
command('tabnext') -- tab 1
|
||||
command('b ' .. files[2]) -- buffer 2
|
||||
eq(10, eval('g:cdprecount'))
|
||||
eq(10, eval('g:cdcount'))
|
||||
eq(11, eval('g:cdprecount'))
|
||||
eq(11, eval('g:cdcount'))
|
||||
command('tabnext') -- tab 2 (has the *same* CWD)
|
||||
eq(10, eval('g:cdprecount')) -- same CWD, no DirChangedPre event
|
||||
eq(10, eval('g:cdcount')) -- same CWD, no DirChanged event
|
||||
eq(11, eval('g:cdprecount')) -- same CWD, no DirChangedPre event
|
||||
eq(11, eval('g:cdcount')) -- same CWD, no DirChanged event
|
||||
|
||||
if is_os('win') then
|
||||
command('tabnew') -- tab 3
|
||||
eq(10, eval('g:cdprecount')) -- same CWD, no DirChangedPre event
|
||||
eq(10, eval('g:cdcount')) -- same CWD, no DirChanged event
|
||||
eq(11, eval('g:cdprecount')) -- same CWD, no DirChangedPre event
|
||||
eq(11, eval('g:cdcount')) -- same CWD, no DirChanged event
|
||||
command('tcd ' .. dirs[2])
|
||||
eq(10, eval('g:cdprecount')) -- same CWD, no DirChangedPre event
|
||||
eq(10, eval('g:cdcount')) -- same CWD, no DirChanged event
|
||||
eq(11, eval('g:cdprecount')) -- same CWD, no DirChangedPre event
|
||||
eq(11, eval('g:cdcount')) -- same CWD, no DirChanged event
|
||||
command('tabnext') -- tab 1
|
||||
eq(10, eval('g:cdprecount')) -- same CWD, no DirChangedPre event
|
||||
eq(10, eval('g:cdcount')) -- same CWD, no DirChanged event
|
||||
eq(11, eval('g:cdprecount')) -- same CWD, no DirChangedPre event
|
||||
eq(11, eval('g:cdcount')) -- same CWD, no DirChanged event
|
||||
command('tabprevious') -- tab 3
|
||||
eq(10, eval('g:cdprecount')) -- same CWD, no DirChangedPre event
|
||||
eq(10, eval('g:cdcount')) -- same CWD, no DirChanged event
|
||||
eq(11, eval('g:cdprecount')) -- same CWD, no DirChangedPre event
|
||||
eq(11, eval('g:cdcount')) -- same CWD, no DirChanged event
|
||||
command('tabprevious') -- tab 2
|
||||
eq(10, eval('g:cdprecount')) -- same CWD, no DirChangedPre event
|
||||
eq(10, eval('g:cdcount')) -- same CWD, no DirChanged event
|
||||
eq(11, eval('g:cdprecount')) -- same CWD, no DirChangedPre event
|
||||
eq(11, eval('g:cdcount')) -- same CWD, no DirChanged event
|
||||
command('tabprevious') -- tab 1
|
||||
eq(10, eval('g:cdprecount')) -- same CWD, no DirChangedPre event
|
||||
eq(10, eval('g:cdcount')) -- same CWD, no DirChanged event
|
||||
eq(11, eval('g:cdprecount')) -- same CWD, no DirChangedPre event
|
||||
eq(11, eval('g:cdcount')) -- same CWD, no DirChanged event
|
||||
command('bcd ' .. dirs[2]) -- buffer 2
|
||||
eq(10, eval('g:cdprecount')) -- same CWD, no DirChangedPre event
|
||||
eq(10, eval('g:cdcount')) -- same CWD, no DirChanged event
|
||||
eq(11, eval('g:cdprecount')) -- same CWD, no DirChangedPre event
|
||||
eq(11, eval('g:cdcount')) -- same CWD, no DirChanged event
|
||||
command('tabnext') -- tab 2
|
||||
eq(10, eval('g:cdprecount')) -- same CWD, no DirChangedPre event
|
||||
eq(10, eval('g:cdcount')) -- same CWD, no DirChanged event
|
||||
eq(11, eval('g:cdprecount')) -- same CWD, no DirChangedPre event
|
||||
eq(11, eval('g:cdcount')) -- same CWD, no DirChanged event
|
||||
command('tabnext') -- tab 3
|
||||
eq(10, eval('g:cdprecount')) -- same CWD, no DirChangedPre event
|
||||
eq(10, eval('g:cdcount')) -- same CWD, no DirChanged event
|
||||
eq(11, eval('g:cdprecount')) -- same CWD, no DirChangedPre event
|
||||
eq(11, eval('g:cdcount')) -- same CWD, no DirChanged event
|
||||
command('tabnext') -- tab 1
|
||||
eq(10, eval('g:cdprecount')) -- same CWD, no DirChangedPre event
|
||||
eq(10, eval('g:cdcount')) -- same CWD, no DirChanged event
|
||||
eq(11, eval('g:cdprecount')) -- same CWD, no DirChangedPre event
|
||||
eq(11, eval('g:cdcount')) -- same CWD, no DirChanged event
|
||||
command('tabprevious') -- tab 3
|
||||
eq(10, eval('g:cdprecount')) -- same CWD, no DirChangedPre event
|
||||
eq(10, eval('g:cdcount')) -- same CWD, no DirChanged event
|
||||
eq(11, eval('g:cdprecount')) -- same CWD, no DirChangedPre event
|
||||
eq(11, eval('g:cdcount')) -- same CWD, no DirChanged event
|
||||
end
|
||||
end)
|
||||
|
||||
it('are triggered by switching to buf/win with different CWD', function()
|
||||
it('triggered by switching to buf/win with different CWD', function()
|
||||
command('lcd ' .. dirs[3]) -- window 3
|
||||
command(('split %s/file'):format(dirs[2])) -- window 2
|
||||
command('lcd ' .. dirs[2])
|
||||
@@ -511,7 +511,7 @@ describe('autocmd DirChanged and DirChangedPre', function()
|
||||
eq(8, eval('g:cdcount')) -- no DirChanged event, window-local CWD has higher priority
|
||||
end)
|
||||
|
||||
it('are triggered by nvim_set_current_dir()', function()
|
||||
it('triggered by nvim_set_current_dir()', function()
|
||||
request('nvim_set_current_dir', dirs[1])
|
||||
eq({ directory = dirs[1], scope = 'global', changed_window = false }, eval('g:evpre'))
|
||||
eq({ cwd = dirs[1], scope = 'global', changed_window = false }, eval('g:ev'))
|
||||
|
||||
@@ -179,13 +179,9 @@ for _, cmd in ipairs { 'cd', 'chdir' } do
|
||||
eq(globalDir, cwd())
|
||||
eq(0, blwd())
|
||||
|
||||
-- A new buffer created with :edit inherits the buffer-local directory; editing an
|
||||
-- existing buffer keeps that buffer's own directory.
|
||||
-- A new buffer created with :edit does not inherit the buffer-local directory.
|
||||
command('b# ')
|
||||
command(('e %s3'):format(tmpfile))
|
||||
eq(1, blwd())
|
||||
eq(join(globalDir, directories.buffer), cwd())
|
||||
command(('e ..%s%s1'):format(pathsep, tmpfile))
|
||||
eq(0, blwd())
|
||||
eq(globalDir, cwd())
|
||||
|
||||
@@ -406,26 +402,32 @@ for _, cmd in ipairs { 'bcd', 'bchdir' } do
|
||||
command('bd') -- delete buffer
|
||||
end)
|
||||
|
||||
it('makes :new/:vnew/:enew use the buffer-local directory', function()
|
||||
it('buffer-local directory is NOT sticky/inherited', function()
|
||||
local bufdir = join(directories.start, directories.buffer)
|
||||
|
||||
command('edit ' .. tmpfile)
|
||||
command(('%s %s'):format(cmd, directories.buffer))
|
||||
|
||||
command(':new')
|
||||
eq(bufdir, cwd())
|
||||
command('wincmd x') -- close :new window
|
||||
|
||||
command(':vnew')
|
||||
eq(bufdir, cwd())
|
||||
command('wincmd x') -- close :vnew window
|
||||
|
||||
command(':enew')
|
||||
eq(bufdir, cwd())
|
||||
|
||||
-- Also in a split.
|
||||
command(':vsplit')
|
||||
-- A new buffer starts without a buffer-local directory.
|
||||
command('new')
|
||||
eq(directories.start, cwd())
|
||||
eq(0, blwd())
|
||||
command('close')
|
||||
eq(bufdir, cwd())
|
||||
command(':enew')
|
||||
command('enew')
|
||||
eq(directories.start, cwd())
|
||||
eq(0, blwd())
|
||||
command('b# ')
|
||||
eq(bufdir, cwd())
|
||||
|
||||
-- Recycling an empty unnamed buffer (:edit) drops its directory with it.
|
||||
command('enew')
|
||||
command(('%s %s'):format(cmd, directories.buffer))
|
||||
eq(bufdir, cwd())
|
||||
command('edit ' .. tmpfile .. '2')
|
||||
eq(directories.start, cwd())
|
||||
eq(0, blwd())
|
||||
end)
|
||||
end)
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user