mirror of
https://github.com/neovim/neovim.git
synced 2025-02-25 18:55:25 -06:00
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:
commit
a1e8199fff
@ -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
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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},
|
||||
|
@ -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;
|
||||
|
@ -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"));
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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 });
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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()
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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. */
|
||||
|
@ -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()
|
||||
|
Loading…
Reference in New Issue
Block a user