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:
Justin M. Keyes
2026-08-04 17:13:32 +02:00
parent 46ca236525
commit 9cd4dd1c19
14 changed files with 140 additions and 135 deletions

View File

@@ -734,7 +734,7 @@ vim.o.cmp = vim.o.casemap
vim.go.casemap = vim.o.casemap
vim.go.cmp = vim.go.casemap
--- When on, `:cd`, `:tcd` and `:lcd` without an argument changes the
--- When on, `:cd`, `:tcd`, `:lcd` and `:bcd` without an argument changes the
--- current working directory to the `$HOME` directory like in Unix.
--- When off, those commands just print the current directory name.
---
@@ -745,7 +745,7 @@ vim.go.cdhome = vim.o.cdhome
vim.go.cdh = vim.go.cdhome
--- This is a list of directories which will be searched when using the
--- `:cd`, `:tcd` and `:lcd` commands, provided that the directory being
--- `:cd`, `:tcd`, `:lcd` and `:bcd` commands, provided that the directory being
--- searched for has a relative path, not an absolute part starting with
--- "/", "./" or "../", the 'cdpath' option is not used then.
--- The 'cdpath' option's value has the same form and semantics as
@@ -1572,7 +1572,7 @@ vim.bo.ci = vim.bo.copyindent
--- following occurrence.
--- *cpo-~*
--- ~ When included, don't resolve symbolic links when
--- changing directory with `:cd`, `:lcd`, or `:tcd`.
--- changing directory with `:cd`, `:tcd`, `:lcd`, or `:bcd`.
--- This preserves the symbolic link path in buffer names
--- and when displaying the current directory. When
--- excluded (default), symbolic links are resolved to

View File

@@ -960,8 +960,8 @@ function vim.fn.charidx(string, idx, countcc, utf16) end
---
--- If {scope} is present, changes the current working directory
--- for the specified scope:
--- "window" Changes the window local directory. |:lcd|
--- "buffer" Changes the buffer local directory. |:bcd|
--- "window" Changes the window local directory. |:lcd|
--- "tabpage" Changes the tabpage local directory. |:tcd|
--- "global" Changes the global directory. |:cd|
---
@@ -3326,23 +3326,27 @@ function vim.fn.getcursorcharpos(winid) end
--- is ignored.
---
--- Tabs, windows and buffers are identified by their respective
--- numbers, 0 means current tab or window or buffer. Missing tab
--- number
--- implies 0. Thus the following are equivalent: >vim
--- numbers, 0 means current tab/window/buffer. Missing {tabnr}
--- implies 0 (missing {bufnr} does not; see below). Thus the
--- following are equivalent: >vim
--- getcwd(0)
--- getcwd(0, 0)
--- <If {winnr} is -1 it is ignored, only the tab is resolved.
--- {winnr} is a |window-number| or |window-ID|.
---
--- If both {winnr} and {tabnr} are -1 and {bufnr} is missing the
--- global working directory is returned.
---
--- Note: When {tabnr} is -1 Vim returns an empty string to
--- signal that it is invalid, whereas Nvim returns either the
--- global working directory if {winnr} is -1 or the working
--- directory of the window indicated by {winnr}.
---
--- If {bufnr} is provided, {winnr} and {tabnr} must be -1 and the
--- working directory of that buffer is returned. An argument may
--- be -1 only if all preceding arguments are -1.
--- If {bufnr} is provided, {winnr} and {tabnr} must be -1, then
--- the buffer-local working directory is returned.
---
--- An argument may be -1 only if all preceding arguments are -1.
---
--- Examples of buffer usage: >vim
--- getcwd(-1, -1, 0) " Get current buffer's directory
--- getcwd(-1, -1, 3) " Get directory of buffer #3
@@ -4468,8 +4472,9 @@ function vim.fn.has_key(dict, key) end
--- and {bufnr} has set a local path via |:bcd|, otherwise 0.
---
--- Tabs, windows and buffers are identified by their respective
--- numbers, 0 means current tab, window or buffer. Missing
--- argument implies 0. Thus the following are equivalent: >vim
--- numbers, 0 means current tab/window/buffer. Missing {winnr}
--- or {tabnr} implies 0 (missing {bufnr} does not; see below).
--- Thus the following are equivalent: >vim
--- echo haslocaldir()
--- echo haslocaldir(0)
--- echo haslocaldir(0, 0)