aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--runtime/doc/autocmd.txt6
-rw-r--r--runtime/doc/editing.txt29
-rw-r--r--runtime/doc/eval.txt25
-rw-r--r--runtime/doc/usr_22.txt24
-rw-r--r--runtime/doc/usr_41.txt3
-rw-r--r--runtime/doc/vim_diff.txt5
-rw-r--r--src/nvim/buffer.c5
-rw-r--r--src/nvim/buffer_defs.h5
-rw-r--r--src/nvim/eval.lua1
-rw-r--r--src/nvim/eval/funcs.c64
-rw-r--r--src/nvim/ex_docmd.c151
-rw-r--r--src/nvim/ex_session.c2
-rw-r--r--src/nvim/file_search.c45
-rw-r--r--src/nvim/globals.h14
-rw-r--r--src/nvim/testdir/test_autochdir.vim10
-rw-r--r--src/nvim/testdir/test_autocmd.vim73
-rw-r--r--src/nvim/testdir/test_cd.vim138
-rw-r--r--src/nvim/testdir/test_find_complete.vim8
-rw-r--r--src/nvim/testdir/test_findfile.vim4
-rw-r--r--src/nvim/testdir/test_getcwd.vim2
-rw-r--r--src/nvim/window.c13
-rw-r--r--test/functional/autocmd/dirchanged_spec.lua120
22 files changed, 615 insertions, 132 deletions
diff --git a/runtime/doc/autocmd.txt b/runtime/doc/autocmd.txt
index 5923dada43..6c41dd3b10 100644
--- a/runtime/doc/autocmd.txt
+++ b/runtime/doc/autocmd.txt
@@ -519,11 +519,17 @@ DiffUpdated After diffs have been updated. Depending on
change or when doing |:diffupdate|.
*DirChanged*
DirChanged After the |current-directory| was changed.
+ The pattern can be:
+ "window" to trigger on `:lcd`
+ "tabpage" to trigger on `:tcd`
+ "global" to trigger on `:cd`
+ "auto" to trigger on 'autochdir'.
Sets these |v:event| keys:
cwd: current working directory
scope: "global", "tab", "window"
changed_window: v:true if we fired the event
switching window (or tab)
+ <afile> is set to the new directory name.
Non-recursive (event cannot trigger itself).
*FileAppendCmd*
FileAppendCmd Before appending to a file. Should do the
diff --git a/runtime/doc/editing.txt b/runtime/doc/editing.txt
index f91e4f0627..efba9020f9 100644
--- a/runtime/doc/editing.txt
+++ b/runtime/doc/editing.txt
@@ -1275,10 +1275,12 @@ exist, the next-higher scope in the hierarchy applies.
*:chd* *:chdir*
:chd[ir][!] [path] Same as |:cd|.
- *:tc* *:tcd* *E5000* *E5001* *E5002*
-:tc[d][!] {path} Like |:cd|, but set the current directory for the
- current tab and window. The current directory for
- other tabs and windows is not changed.
+ *:tc* *:tcd*
+:tc[d][!] {path} Like |:cd|, but only set the directory for the current
+ tab. The current window will also use this directory.
+ The current directory is not changed for windows in
+ other tabs and for windows in the current tab that
+ have their own window-local directory.
*:tcd-*
:tc[d][!] - Change to the previous current directory (before the
@@ -1302,9 +1304,24 @@ exist, the next-higher scope in the hierarchy applies.
*:pw* *:pwd* *E187*
:pw[d] Print the current directory name.
Also see |getcwd()|.
+ *:pwd-verbose*
+ When 'verbose' is non-zero, |:pwd| will also display
+ what scope the current directory was set. Example: >
-So long as no |:tcd| or |:lcd| command has been used, all windows share the
-same "current directory". Using a command to jump to another window doesn't
+ " Set by :cd
+ :verbose pwd
+ [global] /path/to/current
+
+ " Set by :lcd
+ :verbose pwd
+ [window] /path/to/current
+
+ " Set by :tcd
+ :verbose pwd
+ [tabpage] /path/to/current
+
+So long as no |:lcd| or |:tcd| command has been used, all windows share the
+same current directory. Using a command to jump to another window doesn't
change anything for the current directory.
When |:lcd| has been used for a window, the specified directory becomes the
diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt
index e956ccaa77..4467afaa16 100644
--- a/runtime/doc/eval.txt
+++ b/runtime/doc/eval.txt
@@ -2316,6 +2316,7 @@ chansend({id}, {data}) Number Writes {data} to channel
char2nr({expr}[, {utf8}]) Number ASCII/UTF-8 value of first char in {expr}
charidx({string}, {idx} [, {countcc}])
Number char index of byte {idx} in {string}
+chdir({dir}) String change current working directory
cindent({lnum}) Number C indent for line {lnum}
clearmatches([{win}]) none clear all matches
col({expr}) Number column nr of cursor or mark
@@ -2461,6 +2462,7 @@ has({feature}) Number |TRUE| if feature {feature} supported
has_key({dict}, {key}) Number |TRUE| if {dict} has entry {key}
haslocaldir([{winnr} [, {tabnr}]])
Number |TRUE| if current window executed |:lcd|
+ or |:tcd|
hasmapto({what} [, {mode} [, {abbr}]])
Number |TRUE| if mapping to {what} exists
histadd({history}, {item}) String add an item to a history
@@ -3280,6 +3282,27 @@ charidx({string}, {idx} [, {countcc}])
echo charidx('áb́ć', 6, 1) returns 4
echo charidx('áb́ć', 16) returns -1
+chdir({dir}) *chdir()*
+ Change the current working directory to {dir}. The scope of
+ the directory change depends on the directory of the current
+ window:
+ - If the current window has a window-local directory
+ (|:lcd|), then changes the window local directory.
+ - Otherwise, if the current tabpage has a local
+ directory (|:tcd|) then changes the tabpage local
+ directory.
+ - Otherwise, changes the global directory.
+ If successful, returns the previous working directory. Pass
+ this to another chdir() to restore the directory.
+ On failure, returns an empty string.
+
+ Example: >
+ let save_dir = chdir(newdir)
+ if save_dir
+ " ... do some work
+ call chdir(save_dir)
+ endif
+<
cindent({lnum}) *cindent()*
Get the amount of indent for line {lnum} according the C
indenting rules, as with 'cindent'.
@@ -4987,6 +5010,8 @@ getcwd([{winnr}[, {tabnr}]]) *getcwd()*
getcwd(0, 0)
< If {winnr} is -1 it is ignored, only the tab is resolved.
{winnr} can be the window number or the |window-ID|.
+ If both {winnr} and {tabnr} are -1 the global working
+ directory is returned.
Can also be used as a |method|: >
GetWinnr()->getcwd()
diff --git a/runtime/doc/usr_22.txt b/runtime/doc/usr_22.txt
index 56fe5ada2b..f53d578456 100644
--- a/runtime/doc/usr_22.txt
+++ b/runtime/doc/usr_22.txt
@@ -202,14 +202,28 @@ the other window. This is called a local directory. >
:pwd
/home/Bram/VeryLongFileName
-So long as no ":lcd" command has been used, all windows share the same current
-directory. Doing a ":cd" command in one window will also change the current
+So long as no `:lcd` command has been used, all windows share the same current
+directory. Doing a `:cd` command in one window will also change the current
directory of the other window.
- For a window where ":lcd" has been used a different current directory is
-remembered. Using ":cd" or ":lcd" in other windows will not change it.
- When using a ":cd" command in a window that uses a different current
+ For a window where `:lcd` has been used a different current directory is
+remembered. Using `:cd` or `:lcd` in other windows will not change it.
+ When using a `:cd` command in a window that uses a different current
directory, it will go back to using the shared directory.
+
+TAB LOCAL DIRECTORY
+
+When you open a new tab page, it uses the directory of the window in the
+previous tab page from which the new tab page was opened. You can change the
+directory of the current tab page using the `:tcd` command. All the windows in
+a tab page share this directory except for windows with a window-local
+directory. Any new windows opened in this tab page will use this directory as
+the current working directory. Using a `:cd` command in a tab page will not
+change the working directory of tab pages which have a tab local directory.
+When the global working directory is changed using the ":cd" command in a tab
+page, it will also change the current tab page working directory.
+
+
==============================================================================
*22.3* Finding a file
diff --git a/runtime/doc/usr_41.txt b/runtime/doc/usr_41.txt
index c575dd9fd8..6a9284dac9 100644
--- a/runtime/doc/usr_41.txt
+++ b/runtime/doc/usr_41.txt
@@ -785,9 +785,10 @@ System functions and manipulation of files:
isdirectory() check if a directory exists
getfsize() get the size of a file
getcwd() get the current working directory
- haslocaldir() check if current window used |:lcd|
+ haslocaldir() check if current window used |:lcd| or |:tcd|
tempname() get the name of a temporary file
mkdir() create a new directory
+ chdir() change current working directory
delete() delete a file
rename() rename a file
system() get the result of a shell command as a string
diff --git a/runtime/doc/vim_diff.txt b/runtime/doc/vim_diff.txt
index 64824b2e3f..7e7bb7d62f 100644
--- a/runtime/doc/vim_diff.txt
+++ b/runtime/doc/vim_diff.txt
@@ -180,6 +180,7 @@ Commands:
|:match| can be invoked before highlight group is defined
Events:
+ |DirChanged| can be triggered when switching to another window
|Signal|
|TabNewEntered|
|TermClose|
@@ -196,6 +197,10 @@ Functions:
|stdpath()|
|system()|, |systemlist()| can run {cmd} directly (without 'shell')
|matchadd()| can be called before highlight group is defined
+ |getcwd()| and |haslocaldir()| may throw errors. *E5000* *E5001* *E5002*
+ |haslocaldir()|'s only possible return values are 0 and 1, it never returns 2.
+ `getcwd(-1)` is equivalent to `getcwd(-1, 0)` instead of returning the global
+ working directory. Use `getcwd(-1, -1)` to get the global working directory.
Highlight groups:
|highlight-blend| controls blend level for a highlight group
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. */
diff --git a/test/functional/autocmd/dirchanged_spec.lua b/test/functional/autocmd/dirchanged_spec.lua
index 6f2da24cf2..f4a1642ebf 100644
--- a/test/functional/autocmd/dirchanged_spec.lua
+++ b/test/functional/autocmd/dirchanged_spec.lua
@@ -6,6 +6,7 @@ local command = h.command
local eq = h.eq
local eval = h.eval
local request = h.request
+local iswin = h.iswin
describe('autocmd DirChanged', function()
local curdir = string.gsub(lfs.currentdir(), '\\', '/')
@@ -14,6 +15,11 @@ describe('autocmd DirChanged', function()
curdir .. '/Xtest-functional-autocmd-dirchanged.dir2',
curdir .. '/Xtest-functional-autocmd-dirchanged.dir3',
}
+ local win_dirs = {
+ curdir .. '\\XTEST-FUNCTIONAL-AUTOCMD-DIRCHANGED.DIR1',
+ curdir .. '\\XTEST-FUNCTIONAL-AUTOCMD-DIRCHANGED.DIR2',
+ curdir .. '\\XTEST-FUNCTIONAL-AUTOCMD-DIRCHANGED.DIR3',
+ }
setup(function() for _, dir in pairs(dirs) do h.mkdir(dir) end end)
teardown(function() for _, dir in pairs(dirs) do h.rmdir(dir) end end)
@@ -27,17 +33,20 @@ describe('autocmd DirChanged', function()
command([[autocmd DirChanged * let g:getcwd = substitute(g:getcwd, '\\', '/', 'g')]])
end)
- it('sets v:event', function()
+ it('sets v:event and <amatch>', function()
command('lcd '..dirs[1])
eq({cwd=dirs[1], scope='window', changed_window=false}, eval('g:ev'))
+ eq('window', eval('g:amatch'))
eq(1, eval('g:cdcount'))
command('tcd '..dirs[2])
- eq({cwd=dirs[2], scope='tab', changed_window=false}, eval('g:ev'))
+ eq({cwd=dirs[2], scope='tabpage', changed_window=false}, eval('g:ev'))
+ eq('tabpage', eval('g:amatch'))
eq(2, eval('g:cdcount'))
command('cd '..dirs[3])
eq({cwd=dirs[3], scope='global', changed_window=false}, eval('g:ev'))
+ eq('global', eval('g:amatch'))
eq(3, eval('g:cdcount'))
end)
@@ -63,17 +72,6 @@ describe('autocmd DirChanged', function()
eq(dirs[3], eval('getcwd()'))
end)
- it('sets <amatch> to CWD "scope"', function()
- command('lcd '..dirs[1])
- eq('window', eval('g:amatch'))
-
- command('tcd '..dirs[2])
- eq('tab', eval('g:amatch'))
-
- command('cd '..dirs[3])
- eq('global', eval('g:amatch'))
- end)
-
it('does not trigger if :cd fails', function()
command('let g:ev = {}')
@@ -106,13 +104,79 @@ describe('autocmd DirChanged', function()
command('split '..dirs[1]..'/foo')
eq({cwd=dirs[1], scope='window', changed_window=false}, eval('g:ev'))
+ eq('auto', eval('g:amatch'))
command('split '..dirs[2]..'/bar')
eq({cwd=dirs[2], scope='window', changed_window=false}, eval('g:ev'))
+ eq('auto', eval('g:amatch'))
eq(2, eval('g:cdcount'))
end)
+ it('does not trigger if directory has not changed', function()
+ command('lcd '..dirs[1])
+ eq({cwd=dirs[1], scope='window', changed_window=false}, eval('g:ev'))
+ eq('window', eval('g:amatch'))
+ eq(1, eval('g:cdcount'))
+ command('let g:ev = {}')
+ command('lcd '..dirs[1])
+ eq({}, eval('g:ev'))
+ eq(1, eval('g:cdcount'))
+
+ if iswin() then
+ command('lcd '..win_dirs[1])
+ eq({}, eval('g:ev'))
+ eq(1, eval('g:cdcount'))
+ end
+
+ command('tcd '..dirs[2])
+ eq({cwd=dirs[2], scope='tabpage', changed_window=false}, eval('g:ev'))
+ eq('tabpage', eval('g:amatch'))
+ eq(2, eval('g:cdcount'))
+ command('let g:ev = {}')
+ command('tcd '..dirs[2])
+ eq({}, eval('g:ev'))
+ eq(2, eval('g:cdcount'))
+
+ if iswin() then
+ command('tcd '..win_dirs[2])
+ eq({}, eval('g:ev'))
+ eq(2, eval('g:cdcount'))
+ end
+
+ command('cd '..dirs[3])
+ eq({cwd=dirs[3], scope='global', changed_window=false}, eval('g:ev'))
+ eq('global', eval('g:amatch'))
+ eq(3, eval('g:cdcount'))
+ command('let g:ev = {}')
+ command('cd '..dirs[3])
+ eq({}, eval('g:ev'))
+ eq(3, eval('g:cdcount'))
+
+ if iswin() then
+ command('cd '..win_dirs[3])
+ eq({}, eval('g:ev'))
+ eq(3, eval('g:cdcount'))
+ end
+
+ command('set autochdir')
+
+ command('split '..dirs[1]..'/foo')
+ eq({cwd=dirs[1], scope='window', changed_window=false}, eval('g:ev'))
+ eq('auto', eval('g:amatch'))
+ eq(4, eval('g:cdcount'))
+ command('let g:ev = {}')
+ command('split '..dirs[1]..'/bar')
+ eq({}, eval('g:ev'))
+ eq(4, eval('g:cdcount'))
+
+ if iswin() then
+ command('split '..win_dirs[1]..'/baz')
+ eq({}, eval('g:ev'))
+ eq(4, eval('g:cdcount'))
+ end
+ end)
+
it("is triggered by switching to win/tab with different CWD #6054", function()
command('lcd '..dirs[3]) -- window 3
command('split '..dirs[2]..'/foo') -- window 2
@@ -122,6 +186,7 @@ describe('autocmd DirChanged', function()
command('2wincmd w') -- window 2
eq({cwd=dirs[2], scope='window', changed_window=true}, eval('g:ev'))
+ eq('window', eval('g:amatch'))
eq(4, eval('g:cdcount'))
command('tabnew') -- tab 2 (tab-local CWD)
@@ -129,8 +194,10 @@ describe('autocmd DirChanged', function()
command('tcd '..dirs[3])
command('tabnext') -- tab 1 (no tab-local CWD)
eq({cwd=dirs[2], scope='window', changed_window=true}, eval('g:ev'))
+ eq('window', eval('g:amatch'))
command('tabnext') -- tab 2
- eq({cwd=dirs[3], scope='tab', changed_window=true}, eval('g:ev'))
+ eq({cwd=dirs[3], scope='tabpage', changed_window=true}, eval('g:ev'))
+ eq('tabpage', eval('g:amatch'))
eq(7, eval('g:cdcount'))
command('tabnext') -- tab 1
@@ -138,6 +205,31 @@ describe('autocmd DirChanged', function()
eq(9, eval('g:cdcount'))
command('tabnext') -- tab 2 (has the *same* CWD)
eq(9, eval('g:cdcount')) -- same CWD, no DirChanged event
+
+ if iswin() then
+ command('tabnew') -- tab 3
+ eq(9, eval('g:cdcount')) -- same CWD, no DirChanged event
+ command('tcd '..win_dirs[3])
+ eq(9, eval('g:cdcount')) -- same CWD, no DirChanged event
+ command('tabnext') -- tab 1
+ eq(9, eval('g:cdcount')) -- same CWD, no DirChanged event
+ command('tabprevious') -- tab 3
+ eq(9, eval('g:cdcount')) -- same CWD, no DirChanged event
+ command('tabprevious') -- tab 2
+ eq(9, eval('g:cdcount')) -- same CWD, no DirChanged event
+ command('tabprevious') -- tab 1
+ eq(9, eval('g:cdcount')) -- same CWD, no DirChanged event
+ command('lcd '..win_dirs[3]) -- window 3
+ eq(9, eval('g:cdcount')) -- same CWD, no DirChanged event
+ command('tabnext') -- tab 2
+ eq(9, eval('g:cdcount')) -- same CWD, no DirChanged event
+ command('tabnext') -- tab 3
+ eq(9, eval('g:cdcount')) -- same CWD, no DirChanged event
+ command('tabnext') -- tab 1
+ eq(9, eval('g:cdcount')) -- same CWD, no DirChanged event
+ command('tabprevious') -- tab 3
+ eq(9, eval('g:cdcount')) -- same CWD, no DirChanged event
+ end
end)
it('is triggered by nvim_set_current_dir()', function()