mirror of
https://github.com/neovim/neovim.git
synced 2026-09-08 06:57:43 +00:00
fix(cwd): nvim_win_set_buf changes global CWD #41328
Problem:
nvim_win_set_buf() on a non-current win, while the current win has
a win-local dir, changes the global CWD:
:vsplit | lcd ..
:call nvim_win_set_buf(other_win, buf)
:wincmd l
:verbose pwd
[global] /parent " expected: the initial cwd
Analysis:
`globaldir` is where to return when no local dir applies; NULL means the
process CWD is already there. Switching to a window with no local dir
makes update_cwd() chdir back to `globaldir` and clear it. kCtxKeepCwd
restores the process CWD but not that bookkeeping, so the restored
window-local dir is mistaken for the global one.
Solution:
Save/restore `globaldir` with the CWD.
This commit is contained in:
@@ -311,13 +311,14 @@ static void ctx_localdirs_restore(CtxSwitch *cs, win_T *cwp, tabpage_T *tp, bool
|
||||
if (!(persist && cs->cs_globaldir == NULL && globaldir != NULL)) {
|
||||
xfree(globaldir);
|
||||
globaldir = cs->cs_globaldir;
|
||||
cs->cs_globaldir = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/// Saves the dir state to be restored by ctx_dirs_restore():
|
||||
/// - kCtxKeepCwd or kCtxKeepDirs: the CWD, so any directory change caused by switching to `wp`
|
||||
/// ('autochdir', win/tab-local directories) can be undone.
|
||||
/// - kCtxKeepDirs: also copies of the target context's dir scopes (w/b/tp-local, global).
|
||||
/// - kCtxKeepCwd or kCtxKeepDirs: the CWD and `globaldir`, so any directory change caused by
|
||||
/// switching to `wp` ('autochdir', win/tab-local directories) can be undone.
|
||||
/// - kCtxKeepDirs: also copies of the target context's dir scopes (w/b/tp-local).
|
||||
static void ctx_dirs_save(CtxSwitch *cs, win_T *wp, tabpage_T *tp, buf_T *buf)
|
||||
FUNC_ATTR_NONNULL_ARG(1, 2, 3)
|
||||
{
|
||||
@@ -325,6 +326,9 @@ static void ctx_dirs_save(CtxSwitch *cs, win_T *wp, tabpage_T *tp, buf_T *buf)
|
||||
return;
|
||||
}
|
||||
|
||||
// `globaldir` is where to return when no local dir applies (NULL: the CWD is already there).
|
||||
cs->cs_globaldir = globaldir == NULL ? NULL : xstrdup(globaldir);
|
||||
|
||||
// kCtxKeepDirs: also save copies of the target context's dir scopes.
|
||||
if (cs->cs_flags & kCtxKeepDirs) {
|
||||
buf_T *target_buf = buf != NULL ? buf : wp->w_buffer;
|
||||
@@ -332,7 +336,6 @@ static void ctx_dirs_save(CtxSwitch *cs, win_T *wp, tabpage_T *tp, buf_T *buf)
|
||||
cs->cs_w_localdir = wp->w_localdir == NULL ? NULL : xstrdup(wp->w_localdir);
|
||||
cs->cs_b_localdir = target_buf->b_localdir == NULL ? NULL : xstrdup(target_buf->b_localdir);
|
||||
cs->cs_tp_localdir = tp->tp_localdir == NULL ? NULL : xstrdup(tp->tp_localdir);
|
||||
cs->cs_globaldir = globaldir == NULL ? NULL : xstrdup(globaldir);
|
||||
}
|
||||
|
||||
// Getting and setting directory can be slow on some systems, only do this when the current or
|
||||
@@ -367,8 +370,12 @@ static void ctx_dirs_save(CtxSwitch *cs, win_T *wp, tabpage_T *tp, buf_T *buf)
|
||||
/// target window/buffer/tab may have been closed meanwhile.
|
||||
static void ctx_dirs_restore(CtxSwitch *cs)
|
||||
{
|
||||
// kCtxKeepDirs: restore the saved dir scopes. But not for hidden buf (ctx_win).
|
||||
if ((cs->cs_flags & kCtxKeepDirs) && cs->cs_ctxwin_idx < 0) {
|
||||
if (cs->cs_ctxwin_idx >= 0) {
|
||||
return; // Hidden-buffer target: ctx_localdirs_restore() already restored dirs.
|
||||
}
|
||||
|
||||
// kCtxKeepDirs: restore the saved dir scopes.
|
||||
if (cs->cs_flags & kCtxKeepDirs) {
|
||||
tabpage_T *dirs_tab = NULL;
|
||||
FOR_ALL_TABS(tp) {
|
||||
if (tp->handle == cs->cs_dirs_tab) {
|
||||
@@ -377,7 +384,13 @@ static void ctx_dirs_restore(CtxSwitch *cs)
|
||||
}
|
||||
}
|
||||
ctx_localdirs_restore(cs, NULL, dirs_tab, false);
|
||||
} else if (cs->cs_cwd != NULL && !_ctx_did_chdir) {
|
||||
// Pairs with the CWD restore below.
|
||||
xfree(globaldir);
|
||||
globaldir = cs->cs_globaldir;
|
||||
cs->cs_globaldir = NULL;
|
||||
}
|
||||
XFREE_CLEAR(cs->cs_globaldir);
|
||||
|
||||
// Restore the CWD itself.
|
||||
if (cs->cs_apply_acd) {
|
||||
|
||||
@@ -106,9 +106,10 @@ typedef struct {
|
||||
// State kept across the switch:
|
||||
bool cs_did_chdir; ///< saved `ctx_did_chdir` of the enclosing context
|
||||
handle_T cs_dirs_tab; ///< kCtxKeepDirs: tabpage that owns cs_tp_localdir.
|
||||
// Saved dir state. Two users:
|
||||
// Saved dir state. Three users:
|
||||
// 1. hidden-buffer target always saves b/tp/globaldir (so the temp context starts dir-neutral)
|
||||
// 2. kCtxKeepDirs saves copies of all four.
|
||||
// 2. kCtxKeepCwd saves globaldir.
|
||||
// 3. kCtxKeepDirs also saves w/b/tp-localdir.
|
||||
char *cs_w_localdir; ///< Saved w_localdir of the target window
|
||||
char *cs_b_localdir; ///< Saved b_localdir of the target buffer
|
||||
char *cs_tp_localdir; ///< Saved tp_localdir
|
||||
|
||||
Reference in New Issue
Block a user