forked from aniani/vim
patch 8.1.1291: not easy to change directory and restore
Problem: Not easy to change directory and restore. Solution: Add the chdir() function. (Yegappan Lakshmanan, closes #4358)
This commit is contained in:
parent
fd31e45e4b
commit
1063f3d200
@ -2273,6 +2273,7 @@ ch_status({handle} [, {options}])
|
|||||||
String status of channel {handle}
|
String status of channel {handle}
|
||||||
changenr() Number current change number
|
changenr() Number current change number
|
||||||
char2nr({expr} [, {utf8}]) Number ASCII/UTF8 value of first char in {expr}
|
char2nr({expr} [, {utf8}]) Number ASCII/UTF8 value of first char in {expr}
|
||||||
|
chdir({dir}) String change current working directory
|
||||||
cindent({lnum}) Number C indent for line {lnum}
|
cindent({lnum}) Number C indent for line {lnum}
|
||||||
clearmatches([{win}]) none clear all matches
|
clearmatches([{win}]) none clear all matches
|
||||||
col({expr}) Number column nr of cursor or mark
|
col({expr}) Number column nr of cursor or mark
|
||||||
@ -3469,6 +3470,27 @@ char2nr({expr} [, {utf8}]) *char2nr()*
|
|||||||
let list = map(split(str, '\zs'), {_, val -> char2nr(val)})
|
let list = map(split(str, '\zs'), {_, val -> char2nr(val)})
|
||||||
< Result: [65, 66, 67]
|
< Result: [65, 66, 67]
|
||||||
|
|
||||||
|
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()*
|
cindent({lnum}) *cindent()*
|
||||||
Get the amount of indent for line {lnum} according the C
|
Get the amount of indent for line {lnum} according the C
|
||||||
indenting rules, as with 'cindent'.
|
indenting rules, as with 'cindent'.
|
||||||
|
@ -769,6 +769,7 @@ System functions and manipulation of files:
|
|||||||
haslocaldir() check if current window used |:lcd| or |:tcd|
|
haslocaldir() check if current window used |:lcd| or |:tcd|
|
||||||
tempname() get the name of a temporary file
|
tempname() get the name of a temporary file
|
||||||
mkdir() create a new directory
|
mkdir() create a new directory
|
||||||
|
chdir() change current working directory
|
||||||
delete() delete a file
|
delete() delete a file
|
||||||
rename() rename a file
|
rename() rename a file
|
||||||
system() get the result of a shell command as a string
|
system() get the result of a shell command as a string
|
||||||
|
@ -107,6 +107,7 @@ static void f_ch_status(typval_T *argvars, typval_T *rettv);
|
|||||||
#endif
|
#endif
|
||||||
static void f_changenr(typval_T *argvars, typval_T *rettv);
|
static void f_changenr(typval_T *argvars, typval_T *rettv);
|
||||||
static void f_char2nr(typval_T *argvars, typval_T *rettv);
|
static void f_char2nr(typval_T *argvars, typval_T *rettv);
|
||||||
|
static void f_chdir(typval_T *argvars, typval_T *rettv);
|
||||||
static void f_cindent(typval_T *argvars, typval_T *rettv);
|
static void f_cindent(typval_T *argvars, typval_T *rettv);
|
||||||
static void f_clearmatches(typval_T *argvars, typval_T *rettv);
|
static void f_clearmatches(typval_T *argvars, typval_T *rettv);
|
||||||
static void f_col(typval_T *argvars, typval_T *rettv);
|
static void f_col(typval_T *argvars, typval_T *rettv);
|
||||||
@ -597,6 +598,7 @@ static struct fst
|
|||||||
#endif
|
#endif
|
||||||
{"changenr", 0, 0, f_changenr},
|
{"changenr", 0, 0, f_changenr},
|
||||||
{"char2nr", 1, 2, f_char2nr},
|
{"char2nr", 1, 2, f_char2nr},
|
||||||
|
{"chdir", 1, 1, f_chdir},
|
||||||
{"cindent", 1, 1, f_cindent},
|
{"cindent", 1, 1, f_cindent},
|
||||||
{"clearmatches", 0, 1, f_clearmatches},
|
{"clearmatches", 0, 1, f_clearmatches},
|
||||||
{"col", 1, 1, f_col},
|
{"col", 1, 1, f_col},
|
||||||
@ -2490,6 +2492,45 @@ f_char2nr(typval_T *argvars, typval_T *rettv)
|
|||||||
rettv->vval.v_number = tv_get_string(&argvars[0])[0];
|
rettv->vval.v_number = tv_get_string(&argvars[0])[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* "chdir(dir)" function
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
f_chdir(typval_T *argvars, typval_T *rettv)
|
||||||
|
{
|
||||||
|
char_u *cwd;
|
||||||
|
cdscope_T scope = CDSCOPE_GLOBAL;
|
||||||
|
|
||||||
|
rettv->v_type = VAR_STRING;
|
||||||
|
rettv->vval.v_string = NULL;
|
||||||
|
|
||||||
|
if (argvars[0].v_type != VAR_STRING)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Return the current directory
|
||||||
|
cwd = alloc(MAXPATHL);
|
||||||
|
if (cwd != NULL)
|
||||||
|
{
|
||||||
|
if (mch_dirname(cwd, MAXPATHL) != FAIL)
|
||||||
|
{
|
||||||
|
#ifdef BACKSLASH_IN_FILENAME
|
||||||
|
slash_adjust(cwd);
|
||||||
|
#endif
|
||||||
|
rettv->vval.v_string = vim_strsave(cwd);
|
||||||
|
}
|
||||||
|
vim_free(cwd);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (curwin->w_localdir != NULL)
|
||||||
|
scope = CDSCOPE_WINDOW;
|
||||||
|
else if (curtab->tp_localdir != NULL)
|
||||||
|
scope = CDSCOPE_TABPAGE;
|
||||||
|
|
||||||
|
if (!changedir_func(argvars[0].vval.v_string, TRUE, scope))
|
||||||
|
// Directory change failed
|
||||||
|
VIM_CLEAR(rettv->vval.v_string);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* "cindent(lnum)" function
|
* "cindent(lnum)" function
|
||||||
*/
|
*/
|
||||||
|
108
src/ex_docmd.c
108
src/ex_docmd.c
@ -7513,17 +7513,17 @@ free_cd_dir(void)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Deal with the side effects of changing the current directory.
|
* Deal with the side effects of changing the current directory.
|
||||||
* When "tablocal" is TRUE then this was after an ":tcd" command.
|
* When 'scope' is CDSCOPE_TABPAGE then this was after an ":tcd" command.
|
||||||
* When "winlocal" is TRUE then this was after an ":lcd" command.
|
* When 'scope' is CDSCOPE_WINDOW then this was after an ":lcd" command.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
post_chdir(int tablocal, int winlocal)
|
post_chdir(cdscope_T scope)
|
||||||
{
|
{
|
||||||
if (!winlocal)
|
if (scope != CDSCOPE_WINDOW)
|
||||||
// Clear tab local directory for both :cd and :tcd
|
// Clear tab local directory for both :cd and :tcd
|
||||||
VIM_CLEAR(curtab->tp_localdir);
|
VIM_CLEAR(curtab->tp_localdir);
|
||||||
VIM_CLEAR(curwin->w_localdir);
|
VIM_CLEAR(curwin->w_localdir);
|
||||||
if (winlocal || tablocal)
|
if (scope != CDSCOPE_GLOBAL)
|
||||||
{
|
{
|
||||||
/* If still in global directory, need to remember current
|
/* If still in global directory, need to remember current
|
||||||
* directory as global directory. */
|
* directory as global directory. */
|
||||||
@ -7532,7 +7532,7 @@ post_chdir(int tablocal, int winlocal)
|
|||||||
/* Remember this local directory for the window. */
|
/* Remember this local directory for the window. */
|
||||||
if (mch_dirname(NameBuff, MAXPATHL) == OK)
|
if (mch_dirname(NameBuff, MAXPATHL) == OK)
|
||||||
{
|
{
|
||||||
if (tablocal)
|
if (scope == CDSCOPE_TABPAGE)
|
||||||
curtab->tp_localdir = vim_strsave(NameBuff);
|
curtab->tp_localdir = vim_strsave(NameBuff);
|
||||||
else
|
else
|
||||||
curwin->w_localdir = vim_strsave(NameBuff);
|
curwin->w_localdir = vim_strsave(NameBuff);
|
||||||
@ -7548,46 +7548,44 @@ post_chdir(int tablocal, int winlocal)
|
|||||||
shorten_fnames(TRUE);
|
shorten_fnames(TRUE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ":cd", ":tcd", ":lcd", ":chdir" ":tchdir" and ":lchdir".
|
* Change directory function used by :cd/:tcd/:lcd Ex commands and the
|
||||||
|
* chdir() function. If 'winlocaldir' is TRUE, then changes the window-local
|
||||||
|
* directory. If 'tablocaldir' is TRUE, then changes the tab-local directory.
|
||||||
|
* Otherwise changes the global directory.
|
||||||
|
* Returns TRUE if the directory is successfully changed.
|
||||||
*/
|
*/
|
||||||
void
|
int
|
||||||
ex_cd(exarg_T *eap)
|
changedir_func(
|
||||||
|
char_u *new_dir,
|
||||||
|
int forceit,
|
||||||
|
cdscope_T scope)
|
||||||
{
|
{
|
||||||
char_u *new_dir;
|
|
||||||
char_u *tofree;
|
char_u *tofree;
|
||||||
int dir_differs;
|
int dir_differs;
|
||||||
|
int retval = FALSE;
|
||||||
|
|
||||||
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
|
|
||||||
{
|
|
||||||
if (allbuf_locked())
|
if (allbuf_locked())
|
||||||
return;
|
return FALSE;
|
||||||
if (vim_strchr(p_cpo, CPO_CHDIR) != NULL && curbufIsChanged()
|
|
||||||
&& !eap->forceit)
|
if (vim_strchr(p_cpo, CPO_CHDIR) != NULL && curbufIsChanged() && !forceit)
|
||||||
{
|
{
|
||||||
emsg(_("E747: Cannot change directory, buffer is modified (add ! to override)"));
|
emsg(_("E747: Cannot change directory, buffer is modified (add ! to override)"));
|
||||||
return;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ":cd -": Change to previous directory */
|
// ":cd -": Change to previous directory
|
||||||
if (STRCMP(new_dir, "-") == 0)
|
if (STRCMP(new_dir, "-") == 0)
|
||||||
{
|
{
|
||||||
if (prev_dir == NULL)
|
if (prev_dir == NULL)
|
||||||
{
|
{
|
||||||
emsg(_("E186: No previous directory"));
|
emsg(_("E186: No previous directory"));
|
||||||
return;
|
return FALSE;
|
||||||
}
|
}
|
||||||
new_dir = prev_dir;
|
new_dir = prev_dir;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Save current directory for next ":cd -" */
|
// Save current directory for next ":cd -"
|
||||||
tofree = prev_dir;
|
tofree = prev_dir;
|
||||||
if (mch_dirname(NameBuff, MAXPATHL) == OK)
|
if (mch_dirname(NameBuff, MAXPATHL) == OK)
|
||||||
prev_dir = vim_strsave(NameBuff);
|
prev_dir = vim_strsave(NameBuff);
|
||||||
@ -7595,15 +7593,15 @@ ex_cd(exarg_T *eap)
|
|||||||
prev_dir = NULL;
|
prev_dir = NULL;
|
||||||
|
|
||||||
#if defined(UNIX) || defined(VMS)
|
#if defined(UNIX) || defined(VMS)
|
||||||
/* for UNIX ":cd" means: go to home directory */
|
// for UNIX ":cd" means: go to home directory
|
||||||
if (*new_dir == NUL)
|
if (*new_dir == NUL)
|
||||||
{
|
{
|
||||||
/* use NameBuff for home directory name */
|
// use NameBuff for home directory name
|
||||||
# ifdef VMS
|
# ifdef VMS
|
||||||
char_u *p;
|
char_u *p;
|
||||||
|
|
||||||
p = mch_getenv((char_u *)"SYS$LOGIN");
|
p = mch_getenv((char_u *)"SYS$LOGIN");
|
||||||
if (p == NULL || *p == NUL) /* empty is the same as not set */
|
if (p == NULL || *p == NUL) // empty is the same as not set
|
||||||
NameBuff[0] = NUL;
|
NameBuff[0] = NUL;
|
||||||
else
|
else
|
||||||
vim_strncpy(NameBuff, p, MAXPATHL - 1);
|
vim_strncpy(NameBuff, p, MAXPATHL - 1);
|
||||||
@ -7620,30 +7618,56 @@ ex_cd(exarg_T *eap)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
char_u *acmd_fname;
|
char_u *acmd_fname;
|
||||||
int is_winlocal_chdir = eap->cmdidx == CMD_lcd
|
|
||||||
|| eap->cmdidx == CMD_lchdir;
|
|
||||||
int is_tablocal_chdir = eap->cmdidx == CMD_tcd
|
|
||||||
|| eap->cmdidx == CMD_tchdir;
|
|
||||||
|
|
||||||
post_chdir(is_tablocal_chdir, is_winlocal_chdir);
|
post_chdir(scope);
|
||||||
|
|
||||||
/* Echo the new current directory if the command was typed. */
|
|
||||||
if (KeyTyped || p_verbose >= 5)
|
|
||||||
ex_pwd(eap);
|
|
||||||
|
|
||||||
if (dir_differs)
|
if (dir_differs)
|
||||||
{
|
{
|
||||||
if (is_winlocal_chdir)
|
if (scope == CDSCOPE_WINDOW)
|
||||||
acmd_fname = (char_u *)"window";
|
acmd_fname = (char_u *)"window";
|
||||||
else if (is_tablocal_chdir)
|
else if (scope == CDSCOPE_TABPAGE)
|
||||||
acmd_fname = (char_u *)"tabpage";
|
acmd_fname = (char_u *)"tabpage";
|
||||||
else
|
else
|
||||||
acmd_fname = (char_u *)"global";
|
acmd_fname = (char_u *)"global";
|
||||||
apply_autocmds(EVENT_DIRCHANGED, acmd_fname,
|
apply_autocmds(EVENT_DIRCHANGED, acmd_fname, new_dir, FALSE,
|
||||||
new_dir, FALSE, curbuf);
|
curbuf);
|
||||||
}
|
}
|
||||||
|
retval = TRUE;
|
||||||
}
|
}
|
||||||
vim_free(tofree);
|
vim_free(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_T scope = CDSCOPE_GLOBAL;
|
||||||
|
|
||||||
|
if (eap->cmdidx == CMD_lcd || eap->cmdidx == CMD_lchdir)
|
||||||
|
scope = CDSCOPE_WINDOW;
|
||||||
|
else if (eap->cmdidx == CMD_tcd || eap->cmdidx == CMD_tchdir)
|
||||||
|
scope = CDSCOPE_TABPAGE;
|
||||||
|
|
||||||
|
if (changedir_func(new_dir, eap->forceit, scope))
|
||||||
|
{
|
||||||
|
// Echo the new current directory if the command was typed.
|
||||||
|
if (KeyTyped || p_verbose >= 5)
|
||||||
|
ex_pwd(eap);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1032,7 +1032,7 @@ _VimChdir(PyObject *_chdir, PyObject *args, PyObject *kwargs)
|
|||||||
Py_DECREF(newwd);
|
Py_DECREF(newwd);
|
||||||
Py_XDECREF(todecref);
|
Py_XDECREF(todecref);
|
||||||
|
|
||||||
post_chdir(FALSE, FALSE);
|
post_chdir(CDSCOPE_GLOBAL);
|
||||||
|
|
||||||
if (VimTryEnd())
|
if (VimTryEnd())
|
||||||
{
|
{
|
||||||
|
@ -37,7 +37,8 @@ void ex_splitview(exarg_T *eap);
|
|||||||
void tabpage_new(void);
|
void tabpage_new(void);
|
||||||
void do_exedit(exarg_T *eap, win_T *old_curwin);
|
void do_exedit(exarg_T *eap, win_T *old_curwin);
|
||||||
void free_cd_dir(void);
|
void free_cd_dir(void);
|
||||||
void post_chdir(int tablocal, int winlocal);
|
void post_chdir(cdscope_T cdscope);
|
||||||
|
int changedir_func(char_u *new_dir, int forceit, cdscope_T cdscope);
|
||||||
void ex_cd(exarg_T *eap);
|
void ex_cd(exarg_T *eap);
|
||||||
void do_sleep(long msec);
|
void do_sleep(long msec);
|
||||||
void ex_may_print(exarg_T *eap);
|
void ex_may_print(exarg_T *eap);
|
||||||
|
@ -3555,3 +3555,10 @@ typedef struct {
|
|||||||
varnumber_T vv_count;
|
varnumber_T vv_count;
|
||||||
varnumber_T vv_count1;
|
varnumber_T vv_count1;
|
||||||
} vimvars_save_T;
|
} vimvars_save_T;
|
||||||
|
|
||||||
|
// Scope for changing directory
|
||||||
|
typedef enum {
|
||||||
|
CDSCOPE_GLOBAL, // :cd
|
||||||
|
CDSCOPE_TABPAGE, // :tcd
|
||||||
|
CDSCOPE_WINDOW // :lcd
|
||||||
|
} cdscope_T;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
" Test for :cd
|
" Test for :cd and chdir()
|
||||||
|
|
||||||
func Test_cd_large_path()
|
func Test_cd_large_path()
|
||||||
" This used to crash with a heap write overflow.
|
" This used to crash with a heap write overflow.
|
||||||
@ -65,3 +65,44 @@ func Test_cd_with_cpo_chdir()
|
|||||||
set cpo&
|
set cpo&
|
||||||
bw!
|
bw!
|
||||||
endfunc
|
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 chdir('..')
|
||||||
|
call assert_equal('y', fnamemodify(getcwd(1, 2), ':t'))
|
||||||
|
call assert_equal('z', fnamemodify(getcwd(3, 2), ':t'))
|
||||||
|
tabnext | wincmd t
|
||||||
|
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 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)
|
||||||
|
|
||||||
|
only | tabonly
|
||||||
|
exe 'cd ' . topdir
|
||||||
|
call delete('Xdir', 'rf')
|
||||||
|
endfunc
|
||||||
|
@ -767,6 +767,8 @@ static char *(features[]) =
|
|||||||
|
|
||||||
static int included_patches[] =
|
static int included_patches[] =
|
||||||
{ /* Add new patch number below this line */
|
{ /* Add new patch number below this line */
|
||||||
|
/**/
|
||||||
|
1291,
|
||||||
/**/
|
/**/
|
||||||
1290,
|
1290,
|
||||||
/**/
|
/**/
|
||||||
|
Loading…
x
Reference in New Issue
Block a user