Merge pull request #15952 from zeertzjq/vim-8.1.1291

vim-patch:8.0.{1459,1460,1461,1463},8.1.{0602,0604,1291},8.2.{0189,0876,0909,1411}: chdir and DirChanged related patches
This commit is contained in:
Jan Edmund Lazo
2021-10-17 10:26:11 -04:00
committed by GitHub
22 changed files with 615 additions and 132 deletions

View File

@@ -593,9 +593,6 @@ bool close_buffer(win_T *win, buf_T *buf, int action, bool abort_if_last)
buf->b_nwindows--;
}
// Change directories when the 'acd' option is set.
do_autochdir();
// Disable buffer-updates for the current buffer.
// No need to check `unload_buf`: in that case the function returned above.
buf_updates_unload(buf, false);
@@ -1628,7 +1625,7 @@ void do_autochdir(void)
if (p_acd) {
if (starting == 0
&& curbuf->b_ffname != NULL
&& vim_chdirfile(curbuf->b_ffname) == OK) {
&& vim_chdirfile(curbuf->b_ffname, kCdCauseAuto) == OK) {
post_chdir(kCdScopeGlobal, false);
shorten_fnames(true);
}

View File

@@ -953,6 +953,7 @@ struct tabpage_S {
ScopeDictDictItem tp_winvar; ///< Variable for "t:" Dictionary.
dict_T *tp_vars; ///< Internal variables, local to tab page.
char_u *tp_localdir; ///< Absolute path of local cwd or NULL.
char_u *tp_prevdir; ///< Previous directory.
};
/*
@@ -1381,8 +1382,8 @@ struct window_S {
// out of range!)
int w_arg_idx_invalid; // editing another file than w_arg_idx
char_u *w_localdir; /* absolute path of local directory or
NULL */
char_u *w_localdir; // absolute path of local directory or NULL
char_u *w_prevdir; // previous directory
// Options local to a window.
// They are local because they influence the layout of the window or
// depend on the window layout.

View File

@@ -72,6 +72,7 @@ return {
chansend={args=2},
char2nr={args={1, 2}, base=1},
charidx={args={2, 3}},
chdir={args=1, base=1},
cindent={args=1, base=1},
clearmatches={args={0, 1}, base=1},
col={args=1, base=1},

View File

@@ -28,6 +28,7 @@
#include "nvim/file_search.h"
#include "nvim/fileio.h"
#include "nvim/fold.h"
#include "nvim/globals.h"
#include "nvim/if_cscope.h"
#include "nvim/indent.h"
#include "nvim/indent_c.h"
@@ -1062,6 +1063,45 @@ static void f_charidx(typval_T *argvars, typval_T *rettv, FunPtr fptr)
rettv->vval.v_number = len > 0 ? len - 1 : 0;
}
// "chdir(dir)" function
static void f_chdir(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
char_u *cwd;
CdScope scope = kCdScopeGlobal;
rettv->v_type = VAR_STRING;
rettv->vval.v_string = NULL;
if (argvars[0].v_type != VAR_STRING) {
// Returning an empty string means it failed.
// No error message, for historic reasons.
return;
}
// Return the current directory
cwd = xmalloc(MAXPATHL);
if (cwd != NULL) {
if (os_dirname(cwd, MAXPATHL) != FAIL) {
#ifdef BACKSLASH_IN_FILENAME
slash_adjust(cwd);
#endif
rettv->vval.v_string = vim_strsave(cwd);
}
xfree(cwd);
}
if (curwin->w_localdir != NULL) {
scope = kCdScopeWindow;
} else if (curtab->tp_localdir != NULL) {
scope = kCdScopeTabpage;
}
if (!changedir_func(argvars[0].vval.v_string, scope)) {
// Directory change failed
XFREE_CLEAR(rettv->vval.v_string);
}
}
/*
* "cindent(lnum)" function
*/
@@ -3405,8 +3445,8 @@ static void f_getcwd(typval_T *argvars, typval_T *rettv, FunPtr fptr)
// Numbers of the scope objects (window, tab) we want the working directory
// of. A `-1` means to skip this scope, a `0` means the current object.
int scope_number[] = {
[kCdScopeWindow] = 0, // Number of window to look at.
[kCdScopeTab ] = 0, // Number of tab to look at.
[kCdScopeWindow ] = 0, // Number of window to look at.
[kCdScopeTabpage] = 0, // Number of tab to look at.
};
char_u *cwd = NULL; // Current working directory to print
@@ -3449,8 +3489,8 @@ static void f_getcwd(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
// Find the tabpage by number
if (scope_number[kCdScopeTab] > 0) {
tp = find_tabpage(scope_number[kCdScopeTab]);
if (scope_number[kCdScopeTabpage] > 0) {
tp = find_tabpage(scope_number[kCdScopeTabpage]);
if (!tp) {
EMSG(_("E5000: Cannot find tab number."));
return;
@@ -3459,7 +3499,7 @@ static void f_getcwd(typval_T *argvars, typval_T *rettv, FunPtr fptr)
// Find the window in `tp` by number, `NULL` if none.
if (scope_number[kCdScopeWindow] >= 0) {
if (scope_number[kCdScopeTab] < 0) {
if (scope_number[kCdScopeTabpage] < 0) {
EMSG(_("E5001: Higher scope cannot be -1 if lower scope is >= 0."));
return;
}
@@ -3483,7 +3523,7 @@ static void f_getcwd(typval_T *argvars, typval_T *rettv, FunPtr fptr)
break;
}
FALLTHROUGH;
case kCdScopeTab:
case kCdScopeTabpage:
assert(tp);
from = tp->tp_localdir;
if (from) {
@@ -4612,8 +4652,8 @@ static void f_haslocaldir(typval_T *argvars, typval_T *rettv, FunPtr fptr)
// Numbers of the scope objects (window, tab) we want the working directory
// of. A `-1` means to skip this scope, a `0` means the current object.
int scope_number[] = {
[kCdScopeWindow] = 0, // Number of window to look at.
[kCdScopeTab ] = 0, // Number of tab to look at.
[kCdScopeWindow ] = 0, // Number of window to look at.
[kCdScopeTabpage] = 0, // Number of tab to look at.
};
tabpage_T *tp = curtab; // The tabpage to look at.
@@ -4651,8 +4691,8 @@ static void f_haslocaldir(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
// Find the tabpage by number
if (scope_number[kCdScopeTab] > 0) {
tp = find_tabpage(scope_number[kCdScopeTab]);
if (scope_number[kCdScopeTabpage] > 0) {
tp = find_tabpage(scope_number[kCdScopeTabpage]);
if (!tp) {
EMSG(_("E5000: Cannot find tab number."));
return;
@@ -4661,7 +4701,7 @@ static void f_haslocaldir(typval_T *argvars, typval_T *rettv, FunPtr fptr)
// Find the window in `tp` by number, `NULL` if none.
if (scope_number[kCdScopeWindow] >= 0) {
if (scope_number[kCdScopeTab] < 0) {
if (scope_number[kCdScopeTabpage] < 0) {
EMSG(_("E5001: Higher scope cannot be -1 if lower scope is >= 0."));
return;
}
@@ -4680,7 +4720,7 @@ static void f_haslocaldir(typval_T *argvars, typval_T *rettv, FunPtr fptr)
assert(win);
rettv->vval.v_number = win->w_localdir ? 1 : 0;
break;
case kCdScopeTab:
case kCdScopeTabpage:
assert(tp);
rettv->vval.v_number = tp->tp_localdir ? 1 : 0;
break;

View File

@@ -7707,6 +7707,21 @@ void free_cd_dir(void)
#endif
// Get the previous directory for the given chdir scope.
static char_u *get_prevdir(CdScope scope)
{
switch (scope) {
case kCdScopeTabpage:
return curtab->tp_prevdir;
break;
case kCdScopeWindow:
return curwin->w_prevdir;
break;
default:
return prev_dir;
}
}
/// Deal with the side effects of changing the current directory.
///
/// @param scope Scope of the function call (global, tab or window).
@@ -7716,14 +7731,15 @@ void post_chdir(CdScope scope, bool trigger_dirchanged)
XFREE_CLEAR(curwin->w_localdir);
// Overwrite the tab-local CWD for :cd, :tcd.
if (scope >= kCdScopeTab) {
if (scope >= kCdScopeTabpage) {
XFREE_CLEAR(curtab->tp_localdir);
}
if (scope < kCdScopeGlobal) {
char_u *pdir = get_prevdir(scope);
// If still in global directory, set CWD as the global directory.
if (globaldir == NULL && prev_dir != NULL) {
globaldir = vim_strsave(prev_dir);
if (globaldir == NULL && pdir != NULL) {
globaldir = vim_strsave(pdir);
}
}
@@ -7736,7 +7752,7 @@ void post_chdir(CdScope scope, bool trigger_dirchanged)
// We are now in the global directory, no need to remember its name.
XFREE_CLEAR(globaldir);
break;
case kCdScopeTab:
case kCdScopeTabpage:
curtab->tp_localdir = (char_u *)xstrdup(cwd);
break;
case kCdScopeWindow:
@@ -7749,59 +7765,92 @@ void post_chdir(CdScope scope, bool trigger_dirchanged)
shorten_fnames(true);
if (trigger_dirchanged) {
do_autocmd_dirchanged(cwd, scope, false);
do_autocmd_dirchanged(cwd, scope, kCdCauseManual);
}
}
/// `:cd`, `:tcd`, `:lcd`, `:chdir`, `:tchdir` and `:lchdir`.
/// Change directory function used by :cd/:tcd/:lcd Ex commands and the chdir() function.
/// @param new_dir The directory to change to.
/// @param scope Scope of the function call (global, tab or window).
/// @return true if the directory is successfully changed.
bool changedir_func(char_u *new_dir, CdScope scope)
{
char_u *tofree;
char_u *pdir = NULL;
bool retval = false;
if (new_dir == NULL || allbuf_locked()) {
return false;
}
// ":cd -": Change to previous directory
if (STRCMP(new_dir, "-") == 0) {
pdir = get_prevdir(scope);
if (pdir == NULL) {
EMSG(_("E186: No previous directory"));
return false;
}
new_dir = pdir;
}
// Free the previous directory
tofree = get_prevdir(scope);
if (os_dirname(NameBuff, MAXPATHL) == OK) {
pdir = vim_strsave(NameBuff);
} else {
pdir = NULL;
}
switch (scope) {
case kCdScopeTabpage:
curtab->tp_prevdir = pdir;
break;
case kCdScopeWindow:
curwin->w_prevdir = pdir;
break;
default:
prev_dir = pdir;
}
#if defined(UNIX)
// On Unix ":cd" means: go to home directory.
if (*new_dir == NUL) {
// Use NameBuff for home directory name.
expand_env((char_u *)"$HOME", NameBuff, MAXPATHL);
new_dir = NameBuff;
}
#endif
if (vim_chdir(new_dir) == 0) {
bool dir_differs = pdir == NULL || pathcmp((char *)pdir, (char *)new_dir, -1) != 0;
post_chdir(scope, dir_differs);
retval = true;
} else {
EMSG(_(e_failed));
}
xfree(tofree);
return retval;
}
/// ":cd", ":tcd", ":lcd", ":chdir", "tchdir" and ":lchdir".
void ex_cd(exarg_T *eap)
{
char_u *new_dir;
char_u *tofree;
new_dir = eap->arg;
#if !defined(UNIX)
#if !defined(UNIX) && !defined(VMS)
// for non-UNIX ":cd" means: print current directory
if (*new_dir == NUL) {
ex_pwd(NULL);
} else
#endif
{
if (allbuf_locked()) {
return;
}
// ":cd -": Change to previous directory
if (STRCMP(new_dir, "-") == 0) {
if (prev_dir == NULL) {
EMSG(_("E186: No previous directory"));
return;
}
new_dir = prev_dir;
}
// Save current directory for next ":cd -"
tofree = prev_dir;
if (os_dirname(NameBuff, MAXPATHL) == OK) {
prev_dir = vim_strsave(NameBuff);
} else {
prev_dir = NULL;
}
#if defined(UNIX)
// On Unix ":cd" means: go to home directory.
if (*new_dir == NUL) {
// Use NameBuff for home directory name.
expand_env((char_u *)"$HOME", NameBuff, MAXPATHL);
new_dir = NameBuff;
}
#endif
CdScope scope = kCdScopeGlobal; // Depends on command invoked
CdScope scope = kCdScopeGlobal;
switch (eap->cmdidx) {
case CMD_tcd:
case CMD_tchdir:
scope = kCdScopeTab;
scope = kCdScopeTabpage;
break;
case CMD_lcd:
case CMD_lchdir:
@@ -7810,18 +7859,12 @@ void ex_cd(exarg_T *eap)
default:
break;
}
if (vim_chdir(new_dir)) {
EMSG(_(e_failed));
} else {
post_chdir(scope, true);
if (changedir_func(new_dir, scope)) {
// Echo the new current directory if the command was typed.
if (KeyTyped || p_verbose >= 5) {
ex_pwd(eap);
}
}
xfree(tofree);
}
}
@@ -7834,7 +7877,17 @@ static void ex_pwd(exarg_T *eap)
#ifdef BACKSLASH_IN_FILENAME
slash_adjust(NameBuff);
#endif
msg(NameBuff);
if (p_verbose > 0) {
char *context = "global";
if (curwin->w_localdir != NULL) {
context = "window";
} else if (curtab->tp_localdir != NULL) {
context = "tabpage";
}
smsg("[%s] %s", context, (char *)NameBuff);
} else {
msg(NameBuff);
}
} else {
EMSG(_("E187: Unknown"));
}

View File

@@ -963,7 +963,7 @@ void ex_mkrc(exarg_T *eap)
*dirnow = NUL;
}
if (*dirnow != NUL && (ssop_flags & SSOP_SESDIR)) {
if (vim_chdirfile((char_u *)fname) == OK) {
if (vim_chdirfile((char_u *)fname, kCdCauseOther) == OK) {
shorten_fnames(true);
}
} else if (*dirnow != NUL

View File

@@ -54,6 +54,7 @@
#include "nvim/eval.h"
#include "nvim/file_search.h"
#include "nvim/fileio.h"
#include "nvim/globals.h"
#include "nvim/memory.h"
#include "nvim/message.h"
#include "nvim/misc1.h"
@@ -1590,7 +1591,7 @@ theend:
return file_name;
}
void do_autocmd_dirchanged(char *new_dir, CdScope scope, bool changed_window)
void do_autocmd_dirchanged(char *new_dir, CdScope scope, CdCause cause)
{
static bool recursive = false;
@@ -1609,8 +1610,8 @@ void do_autocmd_dirchanged(char *new_dir, CdScope scope, bool changed_window)
case kCdScopeGlobal:
snprintf(buf, sizeof(buf), "global");
break;
case kCdScopeTab:
snprintf(buf, sizeof(buf), "tab");
case kCdScopeTabpage:
snprintf(buf, sizeof(buf), "tabpage");
break;
case kCdScopeWindow:
snprintf(buf, sizeof(buf), "window");
@@ -1620,11 +1621,30 @@ void do_autocmd_dirchanged(char *new_dir, CdScope scope, bool changed_window)
abort();
}
#ifdef BACKSLASH_IN_FILENAME
char new_dir_buf[MAXPATHL];
STRCPY(new_dir_buf, new_dir);
slash_adjust(new_dir_buf);
new_dir = new_dir_buf;
#endif
tv_dict_add_str(dict, S_LEN("scope"), buf); // -V614
tv_dict_add_str(dict, S_LEN("cwd"), new_dir);
tv_dict_add_bool(dict, S_LEN("changed_window"), changed_window);
tv_dict_add_str(dict, S_LEN("cwd"), new_dir);
tv_dict_add_bool(dict, S_LEN("changed_window"), cause == kCdCauseWindow);
tv_dict_set_keys_readonly(dict);
switch (cause) {
case kCdCauseManual:
case kCdCauseWindow:
break;
case kCdCauseAuto:
snprintf(buf, sizeof(buf), "auto");
break;
case kCdCauseOther:
// Should never happen.
abort();
}
apply_autocmds(EVENT_DIRCHANGED, (char_u *)buf, (char_u *)new_dir, false,
curbuf);
@@ -1636,7 +1656,7 @@ void do_autocmd_dirchanged(char *new_dir, CdScope scope, bool changed_window)
/// Change to a file's directory.
/// Caller must call shorten_fnames()!
/// @return OK or FAIL
int vim_chdirfile(char_u *fname)
int vim_chdirfile(char_u *fname, CdCause cause)
{
char dir[MAXPATHL];
@@ -1647,17 +1667,14 @@ int vim_chdirfile(char_u *fname)
NameBuff[0] = NUL;
}
if (os_chdir(dir) != 0) {
if (os_chdir(dir) == 0) {
if (cause != kCdCauseOther && pathcmp(dir, (char *)NameBuff, -1) != 0) {
do_autocmd_dirchanged(dir, kCdScopeWindow, cause);
}
} else {
return FAIL;
}
#ifdef BACKSLASH_IN_FILENAME
slash_adjust((char_u *)dir);
#endif
if (!strequal(dir, (char *)NameBuff)) {
do_autocmd_dirchanged(dir, kCdScopeWindow, false);
}
return OK;
}

View File

@@ -1034,14 +1034,22 @@ typedef enum {
/// directly, use `MIN_CD_SCOPE` and `MAX_CD_SCOPE` instead.
typedef enum {
kCdScopeInvalid = -1,
kCdScopeWindow, ///< Affects one window.
kCdScopeTab, ///< Affects one tab page.
kCdScopeGlobal, ///< Affects the entire Nvim instance.
kCdScopeWindow, ///< Affects one window.
kCdScopeTabpage, ///< Affects one tab page.
kCdScopeGlobal, ///< Affects the entire Nvim instance.
} CdScope;
#define MIN_CD_SCOPE kCdScopeWindow
#define MAX_CD_SCOPE kCdScopeGlobal
/// What caused the current directory to change.
typedef enum {
kCdCauseOther = -1,
kCdCauseManual, ///< Using `:cd`, `:tcd`, `:lcd` or `chdir()`.
kCdCauseWindow, ///< Switching to another window.
kCdCauseAuto, ///< On 'autochdir'.
} CdCause;
// Only filled for Win32.
EXTERN char windowsVersion[20] INIT(= { 0 });

View File

@@ -8,13 +8,21 @@ func Test_set_filename()
let cwd = getcwd()
call test_autochdir()
set acd
let s:li = []
autocmd DirChanged auto call add(s:li, "autocd")
autocmd DirChanged auto call add(s:li, expand("<afile>"))
new
w samples/Xtest
call assert_equal("Xtest", expand('%'))
call assert_equal("samples", substitute(getcwd(), '.*/\(\k*\)', '\1', ''))
call assert_equal(["autocd", getcwd()], s:li)
bwipe!
au! DirChanged
set noacd
exe 'cd ' . cwd
call chdir(cwd)
call delete('samples/Xtest')
endfunc

View File

@@ -42,9 +42,7 @@ if has('timers')
endfunc
func Test_cursorhold_insert_with_timer_interrupt()
if !has('job')
return
endif
CheckFeature job
" Need to move the cursor.
call feedkeys("ggG", "xt")
@@ -551,9 +549,7 @@ endfunc
func Test_OptionSet()
CheckFunction test_override
if !has("eval") || !exists("+autochdir")
return
endif
CheckOption autochdir
call test_override('starting', 1)
set nocp
@@ -1328,6 +1324,71 @@ func Test_autocommand_all_events()
call assert_fails('au * x bwipe', 'E1155:')
endfunc
function s:Before_test_dirchanged()
augroup test_dirchanged
autocmd!
augroup END
let s:li = []
let s:dir_this = getcwd()
let s:dir_foo = s:dir_this . '/Xfoo'
call mkdir(s:dir_foo)
let s:dir_bar = s:dir_this . '/Xbar'
call mkdir(s:dir_bar)
endfunc
function s:After_test_dirchanged()
call chdir(s:dir_this)
call delete(s:dir_foo, 'd')
call delete(s:dir_bar, 'd')
augroup test_dirchanged
autocmd!
augroup END
endfunc
function Test_dirchanged_global()
call s:Before_test_dirchanged()
autocmd test_dirchanged DirChanged global call add(s:li, "cd:")
autocmd test_dirchanged DirChanged global call add(s:li, expand("<afile>"))
call chdir(s:dir_foo)
call assert_equal(["cd:", s:dir_foo], s:li)
call chdir(s:dir_foo)
call assert_equal(["cd:", s:dir_foo], s:li)
exe 'lcd ' .. fnameescape(s:dir_bar)
call assert_equal(["cd:", s:dir_foo], s:li)
call s:After_test_dirchanged()
endfunc
function Test_dirchanged_local()
call s:Before_test_dirchanged()
autocmd test_dirchanged DirChanged window call add(s:li, "lcd:")
autocmd test_dirchanged DirChanged window call add(s:li, expand("<afile>"))
call chdir(s:dir_foo)
call assert_equal([], s:li)
exe 'lcd ' .. fnameescape(s:dir_bar)
call assert_equal(["lcd:", s:dir_bar], s:li)
exe 'lcd ' .. fnameescape(s:dir_bar)
call assert_equal(["lcd:", s:dir_bar], s:li)
call s:After_test_dirchanged()
endfunc
function Test_dirchanged_auto()
CheckFunction test_autochdir
CheckOption autochdir
call s:Before_test_dirchanged()
call test_autochdir()
autocmd test_dirchanged DirChanged auto call add(s:li, "auto:")
autocmd test_dirchanged DirChanged auto call add(s:li, expand("<afile>"))
set acd
cd ..
call assert_equal([], s:li)
exe 'edit ' . s:dir_foo . '/Xfile'
call assert_equal(s:dir_foo, getcwd())
call assert_equal(["auto:", s:dir_foo], s:li)
set noacd
bwipe!
call s:After_test_dirchanged()
endfunc
" Test TextChangedI and TextChangedP
" See test/functional/viml/completion_spec.lua'
func Test_ChangedP()

View File

@@ -12,7 +12,7 @@ func Test_cd_up_and_down()
let path = getcwd()
cd ..
call assert_notequal(path, getcwd())
exe 'cd ' . path
exe 'cd ' .. fnameescape(path)
call assert_equal(path, getcwd())
endfunc
@@ -23,7 +23,7 @@ func Test_cd_no_arg()
cd
call assert_equal($HOME, getcwd())
call assert_notequal(path, getcwd())
exe 'cd ' . path
exe 'cd ' .. fnameescape(path)
call assert_equal(path, getcwd())
else
" Test that cd without argument echoes cwd on non-Unix systems.
@@ -43,6 +43,20 @@ func Test_cd_minus()
call assert_equal(path_dotdot, getcwd())
cd -
call assert_equal(path, getcwd())
" Test for :cd - without a previous directory
let lines =<< trim [SCRIPT]
call assert_fails('cd -', 'E186:')
call assert_fails('call chdir("-")', 'E186:')
call writefile(v:errors, 'Xresult')
qall!
[SCRIPT]
call writefile(lines, 'Xscript')
if RunVim([], [], '--clean -S Xscript')
call assert_equal([], readfile('Xresult'))
endif
call delete('Xscript')
call delete('Xresult')
endfunc
func Test_cd_with_cpo_chdir()
@@ -61,7 +75,7 @@ func Test_cd_with_cpo_chdir()
" :cd should succeed when buffer has been written.
w!
exe 'cd ' . path
exe 'cd ' .. fnameescape(path)
call assert_equal(path, getcwd())
call delete('Xfoo')
@@ -69,6 +83,124 @@ func Test_cd_with_cpo_chdir()
bw!
endfunc
" Test for chdir()
func Test_chdir_func()
let topdir = getcwd()
call mkdir('Xdir/y/z', 'p')
" Create a few tabpages and windows with different directories
new
cd Xdir
tabnew
tcd y
below new
below new
lcd z
tabfirst
call assert_match('^\[global\] .*/Xdir$', trim(execute('verbose pwd')))
call chdir('..')
call assert_equal('y', fnamemodify(getcwd(1, 2), ':t'))
call assert_equal('z', fnamemodify(getcwd(3, 2), ':t'))
tabnext | wincmd t
call assert_match('^\[tabpage\] .*/y$', trim(execute('verbose pwd')))
call chdir('..')
call assert_equal('Xdir', fnamemodify(getcwd(1, 2), ':t'))
call assert_equal('Xdir', fnamemodify(getcwd(2, 2), ':t'))
call assert_equal('z', fnamemodify(getcwd(3, 2), ':t'))
call assert_equal('testdir', fnamemodify(getcwd(1, 1), ':t'))
3wincmd w
call assert_match('^\[window\] .*/z$', trim(execute('verbose pwd')))
call chdir('..')
call assert_equal('Xdir', fnamemodify(getcwd(1, 2), ':t'))
call assert_equal('Xdir', fnamemodify(getcwd(2, 2), ':t'))
call assert_equal('y', fnamemodify(getcwd(3, 2), ':t'))
call assert_equal('testdir', fnamemodify(getcwd(1, 1), ':t'))
" Error case
call assert_fails("call chdir('dir-abcd')", 'E472:')
silent! let d = chdir("dir_abcd")
call assert_equal("", d)
" Should not crash
call chdir(d)
only | tabonly
call chdir(topdir)
call delete('Xdir', 'rf')
endfunc
" Test for changing to the previous directory '-'
func Test_prev_dir()
let topdir = getcwd()
call mkdir('Xdir/a/b/c', 'p')
" Create a few tabpages and windows with different directories
new | only
tabnew | new
tabnew
tabfirst
cd Xdir
tabnext | wincmd t
tcd a
wincmd w
lcd b
tabnext
tcd a/b/c
" Change to the previous directory twice in all the windows.
tabfirst
cd - | cd -
tabnext | wincmd t
tcd - | tcd -
wincmd w
lcd - | lcd -
tabnext
tcd - | tcd -
" Check the directory of all the windows
tabfirst
call assert_equal('Xdir', fnamemodify(getcwd(), ':t'))
tabnext | wincmd t
call assert_equal('a', fnamemodify(getcwd(), ':t'))
wincmd w
call assert_equal('b', fnamemodify(getcwd(), ':t'))
tabnext
call assert_equal('c', fnamemodify(getcwd(), ':t'))
" Change to the previous directory using chdir()
tabfirst
call chdir("-") | call chdir("-")
tabnext | wincmd t
call chdir("-") | call chdir("-")
wincmd w
call chdir("-") | call chdir("-")
tabnext
call chdir("-") | call chdir("-")
" Check the directory of all the windows
tabfirst
call assert_equal('Xdir', fnamemodify(getcwd(), ':t'))
tabnext | wincmd t
call assert_equal('a', fnamemodify(getcwd(), ':t'))
wincmd w
call assert_equal('b', fnamemodify(getcwd(), ':t'))
tabnext
call assert_equal('c', fnamemodify(getcwd(), ':t'))
only | tabonly
call chdir(topdir)
call delete('Xdir', 'rf')
endfunc
func Test_lcd_split()
let curdir = getcwd()
lcd ..
split
lcd -
call assert_equal(curdir, getcwd())
quit!
endfunc
func Test_cd_from_non_existing_dir()
CheckNotMSWindows

View File

@@ -36,7 +36,7 @@ func Test_find_complete()
" We shouldn't find any file till this point
call mkdir('in/path', 'p')
exe 'cd ' . cwd
call chdir(cwd)
call writefile(['Holy Grail'], 'Xfind/file.txt')
call writefile(['Jimmy Hoffa'], 'Xfind/in/file.txt')
call writefile(['Another Holy Grail'], 'Xfind/in/stuff.txt')
@@ -133,12 +133,12 @@ func Test_find_complete()
call assert_equal('Voyager 2', getline(1))
" Check for correct handling of shorten_fname()'s behavior on windows
exec "cd " . cwd . "/Xfind/in"
call chdir(cwd .. "/Xfind/in")
call feedkeys(":find file\t\n", "xt")
call assert_equal('Jimmy Hoffa', getline(1))
" Test for relative to current buffer 'path' item
exec "cd " . cwd . "/Xfind/"
call chdir(cwd . "/Xfind/")
set path=./path
" Open the file where Jimmy Hoffa is found
e in/file.txt
@@ -157,7 +157,7 @@ func Test_find_complete()
call assert_equal('Another Holy Grail', getline(1))
enew | only
exe 'cd ' . cwd
call chdir(cwd)
call delete('Xfind', 'rf')
set path&
endfunc

View File

@@ -113,7 +113,7 @@ func Test_findfile()
call assert_match('.*/Xdir1/bar', findfile('bar', '**;', 2))
bwipe!
exe 'cd ' . save_dir
call chdir(save_dir)
call CleanFiles()
let &path = save_path
let &shellslash = save_shellslash
@@ -171,7 +171,7 @@ func Test_finddir()
call assert_match('.*/Xdir1/Xdir2', finddir('Xdir2', '**;', 2))
call assert_equal('Xdir3', finddir('Xdir3', '**;', 1))
exe 'cd ' . save_dir
call chdir(save_dir)
call CleanFiles()
let &path = save_path
let &shellslash = save_shellslash

View File

@@ -46,7 +46,7 @@ endfunction
let g:cwd=getcwd()
function TearDown()
q
exec "cd " . g:cwd
call chdir(g:cwd)
call delete("Xtopdir", "rf")
endfunction

View File

@@ -23,6 +23,7 @@
#include "nvim/fold.h"
#include "nvim/garray.h"
#include "nvim/getchar.h"
#include "nvim/globals.h"
#include "nvim/hashtab.h"
#include "nvim/main.h"
#include "nvim/mark.h"
@@ -1462,6 +1463,8 @@ static void win_init(win_T *newp, win_T *oldp, int flags)
}
newp->w_localdir = (oldp->w_localdir == NULL)
? NULL : vim_strsave(oldp->w_localdir);
newp->w_prevdir = (oldp->w_prevdir == NULL)
? NULL : vim_strsave(oldp->w_prevdir);
// copy tagstack and folds
for (i = 0; i < oldp->w_tagstacklen; i++) {
@@ -3732,6 +3735,7 @@ void free_tabpage(tabpage_T *tp)
}
xfree(tp->tp_localdir);
xfree(tp->tp_prevdir);
xfree(tp);
}
@@ -4540,9 +4544,9 @@ static void win_enter_ext(win_T *const wp, const int flags)
}
}
if (os_chdir(new_dir) == 0) {
if (!p_acd && !strequal(new_dir, cwd)) {
if (!p_acd && pathcmp(new_dir, cwd, -1) != 0) {
do_autocmd_dirchanged(new_dir, curwin->w_localdir
? kCdScopeWindow : kCdScopeTab, true);
? kCdScopeWindow : kCdScopeTabpage, kCdCauseWindow);
}
shorten_fnames(true);
}
@@ -4550,8 +4554,8 @@ static void win_enter_ext(win_T *const wp, const int flags)
// Window doesn't have a local directory and we are not in the global
// directory: Change to the global directory.
if (os_chdir((char *)globaldir) == 0) {
if (!p_acd && !strequal((char *)globaldir, cwd)) {
do_autocmd_dirchanged((char *)globaldir, kCdScopeGlobal, true);
if (!p_acd && pathcmp((char *)globaldir, cwd, -1) != 0) {
do_autocmd_dirchanged((char *)globaldir, kCdScopeGlobal, kCdCauseWindow);
}
}
XFREE_CLEAR(globaldir);
@@ -4770,6 +4774,7 @@ static void win_free(win_T *wp, tabpage_T *tp)
}
xfree(wp->w_localdir);
xfree(wp->w_prevdir);
/* Remove the window from the b_wininfo lists, it may happen that the
* freed memory is re-used for another window. */