mirror of
https://github.com/neovim/neovim.git
synced 2026-08-25 08:31:51 +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:
@@ -1778,7 +1778,7 @@ void set_curbuf(buf_T *buf, int action, bool update_jumplist)
|
||||
}
|
||||
|
||||
// Maybe cd to buffer-local directory
|
||||
fix_current_dir(false);
|
||||
update_cwd(kCdCauseBuffer);
|
||||
}
|
||||
|
||||
/// Enter a new current buffer.
|
||||
@@ -2031,8 +2031,6 @@ buf_T *buflist_new(char *ffname_arg, char *sfname_arg, linenr_T lnum, int flags)
|
||||
trigger_undo_ftplugin(buf, curwin);
|
||||
// It's like this buffer is deleted. Watch out for autocommands that
|
||||
// change curbuf! If that happens, allocate a new buffer anyway.
|
||||
// We also ask it to not free the buffer-local directory so we can reuse
|
||||
// it.
|
||||
buf_freeall(buf, BFA_WIPE | BFA_DEL);
|
||||
if (aborting()) { // autocmds may abort script processing
|
||||
xfree(ffname);
|
||||
@@ -2162,8 +2160,6 @@ bool curbuf_reusable(void)
|
||||
&& curbuf->b_ffname == NULL
|
||||
&& curbuf->b_nwindows <= 1
|
||||
&& !curbuf->terminal
|
||||
&& curbuf->b_localdir == NULL
|
||||
&& curbuf->b_prevdir == NULL
|
||||
&& (curbuf->b_ml.ml_mfp == NULL || buf_is_empty(curbuf))
|
||||
&& !bt_quickfix(curbuf)
|
||||
&& !curbufIsChanged());
|
||||
|
||||
@@ -583,7 +583,7 @@ void ctx_restore(CtxSwitch *cs)
|
||||
// If :lcd has been used in the autocommand window, correct current
|
||||
// directory before restoring b_localdir, tp_localdir and globaldir.
|
||||
if (cwp->w_localdir != NULL) {
|
||||
fix_current_dir(true);
|
||||
update_cwd(kCdCauseWindow);
|
||||
}
|
||||
if (bufref_valid(&cs->cs_new_curbuf)) {
|
||||
xfree(cs->cs_new_curbuf.br_buf->b_localdir);
|
||||
|
||||
@@ -1265,8 +1265,8 @@ M.funcs = {
|
||||
|
||||
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|
|
||||
|
||||
@@ -4101,23 +4101,27 @@ M.funcs = {
|
||||
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
|
||||
@@ -5435,8 +5439,9 @@ M.funcs = {
|
||||
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)
|
||||
|
||||
@@ -2937,23 +2937,8 @@ int do_ecmd(int fnum, char *ffname, char *sfname, exarg_T *eap, linenr_T newlnum
|
||||
redraw_curbuf_later(UPD_NOT_VALID); // redraw this buffer later
|
||||
}
|
||||
|
||||
// A new buffer inherits the buffer-local directory of the buffer it was created from (unless
|
||||
// autocommands already set one).
|
||||
if (!oldbuf && curbuf->b_localdir == NULL
|
||||
&& bufref_valid(&old_curbuf)
|
||||
&& old_curbuf.br_buf != curbuf
|
||||
&& old_curbuf.br_buf->b_localdir != NULL) {
|
||||
curbuf->b_localdir = xstrdup(old_curbuf.br_buf->b_localdir);
|
||||
if (old_curbuf.br_buf->b_prevdir != NULL) {
|
||||
curbuf->b_prevdir = xstrdup(old_curbuf.br_buf->b_prevdir);
|
||||
}
|
||||
}
|
||||
|
||||
// If editing a buffer in the current window, make sure to update to the
|
||||
// buffer's working directory.
|
||||
if (oldwin == curwin) {
|
||||
fix_current_dir(false);
|
||||
}
|
||||
// Update to the working directory of the new current buffer.
|
||||
update_cwd(kCdCauseBuffer);
|
||||
|
||||
// Change directories when the 'acd' option is set.
|
||||
do_autochdir();
|
||||
|
||||
@@ -1093,7 +1093,7 @@ local options = {
|
||||
doc = [[on on Unix, off on Windows]],
|
||||
},
|
||||
desc = [=[
|
||||
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.
|
||||
]=],
|
||||
@@ -1113,7 +1113,7 @@ local options = {
|
||||
deny_duplicates = true,
|
||||
desc = [=[
|
||||
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
|
||||
@@ -2119,7 +2119,7 @@ local options = {
|
||||
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
|
||||
|
||||
@@ -5300,7 +5300,7 @@ static void win_enter_ext(win_T *const wp, const int flags)
|
||||
win_fix_cursor(get_real_state() & (MODE_NORMAL|MODE_CMDLINE|MODE_TERMINAL));
|
||||
}
|
||||
|
||||
fix_current_dir(true);
|
||||
update_cwd(kCdCauseWindow);
|
||||
|
||||
entering_window(curwin);
|
||||
// Careful: autocommands may close the window and make "wp" invalid
|
||||
@@ -5352,11 +5352,12 @@ static void win_enter_ext(win_T *const wp, const int flags)
|
||||
do_autochdir();
|
||||
}
|
||||
|
||||
/// Used after making another window or buffer the current one: change directory if needed.
|
||||
void fix_current_dir(bool caused_by_win)
|
||||
/// Applies the effective current-directory of the current window (its window-local, buffer-local,
|
||||
/// tab-local or the global directory). Called after setting curbuf/curwin.
|
||||
///
|
||||
/// @param cause What caused the switch, reported by the DirChanged event.
|
||||
void update_cwd(CdCause cause)
|
||||
{
|
||||
CdCause cause = caused_by_win ? kCdCauseWindow : kCdCauseBuffer;
|
||||
|
||||
// New directory is either the local directory of the window, buffer, tab or NULL.
|
||||
char *new_dir;
|
||||
CdScope scope;
|
||||
|
||||
Reference in New Issue
Block a user