diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/nvim/buffer.c | 5 | ||||
-rw-r--r-- | src/nvim/buffer_defs.h | 5 | ||||
-rw-r--r-- | src/nvim/eval.lua | 1 | ||||
-rw-r--r-- | src/nvim/eval/funcs.c | 64 | ||||
-rw-r--r-- | src/nvim/ex_docmd.c | 151 | ||||
-rw-r--r-- | src/nvim/ex_session.c | 2 | ||||
-rw-r--r-- | src/nvim/file_search.c | 45 | ||||
-rw-r--r-- | src/nvim/globals.h | 14 | ||||
-rw-r--r-- | src/nvim/testdir/test_autochdir.vim | 10 | ||||
-rw-r--r-- | src/nvim/testdir/test_autocmd.vim | 73 | ||||
-rw-r--r-- | src/nvim/testdir/test_cd.vim | 138 | ||||
-rw-r--r-- | src/nvim/testdir/test_find_complete.vim | 8 | ||||
-rw-r--r-- | src/nvim/testdir/test_findfile.vim | 4 | ||||
-rw-r--r-- | src/nvim/testdir/test_getcwd.vim | 2 | ||||
-rw-r--r-- | src/nvim/window.c | 13 |
15 files changed, 429 insertions, 106 deletions
diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index 1de54e0736..826a197454 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -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); } diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index 2c411553b8..19b0a3c5c6 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -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. diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua index 70aa2bb1f8..dfc51d80af 100644 --- a/src/nvim/eval.lua +++ b/src/nvim/eval.lua @@ -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}, diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index da129c1170..5faf2b0b81 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -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; diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 5193391442..f649266b9f 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -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`. -void ex_cd(exarg_T *eap) +/// 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 *new_dir; char_u *tofree; + char_u *pdir = NULL; + bool retval = false; - new_dir = eap->arg; -#if !defined(UNIX) - // for non-UNIX ":cd" means: print current directory - if (*new_dir == NUL) { - ex_pwd(NULL); - } else -#endif - { - if (allbuf_locked()) { - return; - } + if (new_dir == NULL || allbuf_locked()) { + return false; + } - // ":cd -": Change to previous directory - if (STRCMP(new_dir, "-") == 0) { - if (prev_dir == NULL) { - EMSG(_("E186: No previous directory")); - return; - } - new_dir = prev_dir; + // ":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; + } - // Save current directory for next ":cd -" - tofree = prev_dir; - if (os_dirname(NameBuff, MAXPATHL) == OK) { - prev_dir = vim_strsave(NameBuff); - } else { - prev_dir = NULL; - } + // 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; - } + // 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 + 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; + new_dir = eap->arg; +#if !defined(UNIX) && !defined(VMS) + // for non-UNIX ":cd" means: print current directory + if (*new_dir == NUL) { + ex_pwd(NULL); + } else +#endif + { + 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")); } diff --git a/src/nvim/ex_session.c b/src/nvim/ex_session.c index 1e1da6a9a3..0dfd7e1edd 100644 --- a/src/nvim/ex_session.c +++ b/src/nvim/ex_session.c @@ -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 diff --git a/src/nvim/file_search.c b/src/nvim/file_search.c index 5458d8acf2..9eab579243 100644 --- a/src/nvim/file_search.c +++ b/src/nvim/file_search.c @@ -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; } diff --git a/src/nvim/globals.h b/src/nvim/globals.h index 8a36b3bedd..73cfffb0fb 100644 --- a/src/nvim/globals.h +++ b/src/nvim/globals.h @@ -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 }); diff --git a/src/nvim/testdir/test_autochdir.vim b/src/nvim/testdir/test_autochdir.vim index d071f4b325..0b76828dd7 100644 --- a/src/nvim/testdir/test_autochdir.vim +++ b/src/nvim/testdir/test_autochdir.vim @@ -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 diff --git a/src/nvim/testdir/test_autocmd.vim b/src/nvim/testdir/test_autocmd.vim index 015979e1be..c350a17236 100644 --- a/src/nvim/testdir/test_autocmd.vim +++ b/src/nvim/testdir/test_autocmd.vim @@ -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() diff --git a/src/nvim/testdir/test_cd.vim b/src/nvim/testdir/test_cd.vim index 02a23bf82f..0bba321ee2 100644 --- a/src/nvim/testdir/test_cd.vim +++ b/src/nvim/testdir/test_cd.vim @@ -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 diff --git a/src/nvim/testdir/test_find_complete.vim b/src/nvim/testdir/test_find_complete.vim index 0a00d9432f..32ca9672ef 100644 --- a/src/nvim/testdir/test_find_complete.vim +++ b/src/nvim/testdir/test_find_complete.vim @@ -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 diff --git a/src/nvim/testdir/test_findfile.vim b/src/nvim/testdir/test_findfile.vim index d92706dbe5..5a20475d3d 100644 --- a/src/nvim/testdir/test_findfile.vim +++ b/src/nvim/testdir/test_findfile.vim @@ -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 diff --git a/src/nvim/testdir/test_getcwd.vim b/src/nvim/testdir/test_getcwd.vim index 2ff396b641..a75583cd2c 100644 --- a/src/nvim/testdir/test_getcwd.vim +++ b/src/nvim/testdir/test_getcwd.vim @@ -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 diff --git a/src/nvim/window.c b/src/nvim/window.c index 5d43c5d284..dfe1ffdbd4 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -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. */ |