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 vim/vim#4358)
1063f3d200

Also includes some documentation changes from patch 8.1.1218.
This commit is contained in:
zeertzjq
2021-10-17 22:04:53 +08:00
parent 57651df9c1
commit 8727d38012
9 changed files with 191 additions and 55 deletions

View File

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

View File

@@ -28,6 +28,7 @@
#include "nvim/file_search.h"
#include "nvim/fileio.h"
#include "nvim/fold.h"
#include "nvim/globals.h"
#include "nvim/if_cscope.h"
#include "nvim/indent.h"
#include "nvim/indent_c.h"
@@ -1062,6 +1063,43 @@ 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) {
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 = kCdScopeTab;
}
if (!changedir_func(argvars[0].vval.v_string, scope)) {
// Directory change failed
XFREE_CLEAR(rettv->vval.v_string);
}
}
/*
* "cindent(lnum)" function
*/

View File

@@ -7753,51 +7753,68 @@ void post_chdir(CdScope scope, bool trigger_dirchanged)
}
}
/// `:cd`, `:tcd`, `:lcd`, `:chdir`, `:tchdir` and `:lchdir`.
/// Change directory function used by :cd/:tcd/:lcd Ex commands and the chdir() function.
/// @return true if the directory is successfully changed.
bool changedir_func(char_u *new_dir, CdScope scope)
{
char_u *tofree;
bool retval = false;
if (allbuf_locked()) {
return false;
}
// ":cd -": Change to previous directory
if (STRCMP(new_dir, "-") == 0) {
if (prev_dir == NULL) {
EMSG(_("E186: No previous directory"));
return false;
}
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
bool dir_differs = prev_dir == NULL || pathcmp((char *)prev_dir, (char *)new_dir, -1) != 0;
if (dir_differs && vim_chdir(new_dir)) {
EMSG(_(e_failed));
} else {
post_chdir(scope, dir_differs);
retval = true;
}
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:
@@ -7810,19 +7827,12 @@ void ex_cd(exarg_T *eap)
default:
break;
}
bool dir_differs = prev_dir == NULL || pathcmp((char *)prev_dir, (char *)new_dir, -1) != 0;
if (dir_differs && vim_chdir(new_dir)) {
EMSG(_(e_failed));
} else {
post_chdir(scope, dir_differs);
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);
}
}

View File

@@ -69,6 +69,47 @@ 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 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
func Test_cd_from_non_existing_dir()
CheckNotMSWindows