vim-patch:8.2.0909: cannot go back to the previous local directory

Problem:    Cannot go back to the previous local directory.
Solution:   Add "tcd -" and "lcd -". (Yegappan Lakshmanan, closes vim/vim#4362)
002bc79991
This commit is contained in:
zeertzjq 2021-10-17 22:04:53 +08:00
parent 34cfe74568
commit 60584c0245
5 changed files with 132 additions and 18 deletions

View File

@ -953,6 +953,7 @@ struct tabpage_S {
ScopeDictDictItem tp_winvar; ///< Variable for "t:" Dictionary. ScopeDictDictItem tp_winvar; ///< Variable for "t:" Dictionary.
dict_T *tp_vars; ///< Internal variables, local to tab page. dict_T *tp_vars; ///< Internal variables, local to tab page.
char_u *tp_localdir; ///< Absolute path of local cwd or NULL. 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!) // out of range!)
int w_arg_idx_invalid; // editing another file than w_arg_idx int w_arg_idx_invalid; // editing another file than w_arg_idx
char_u *w_localdir; /* absolute path of local directory or char_u *w_localdir; // absolute path of local directory or NULL
NULL */ char_u *w_prevdir; // previous directory
// Options local to a window. // Options local to a window.
// They are local because they influence the layout of the window or // They are local because they influence the layout of the window or
// depend on the window layout. // depend on the window layout.

View File

@ -7707,6 +7707,21 @@ void free_cd_dir(void)
#endif #endif
// Get the previous directory for the given chdir scope.
static char_u *get_prevdir(CdScope scope)
{
switch (scope) {
case kCdScopeTab:
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. /// Deal with the side effects of changing the current directory.
/// ///
/// @param scope Scope of the function call (global, tab or window). /// @param scope Scope of the function call (global, tab or window).
@ -7721,9 +7736,10 @@ void post_chdir(CdScope scope, bool trigger_dirchanged)
} }
if (scope < kCdScopeGlobal) { if (scope < kCdScopeGlobal) {
char_u *pdir = get_prevdir(scope);
// If still in global directory, set CWD as the global directory. // If still in global directory, set CWD as the global directory.
if (globaldir == NULL && prev_dir != NULL) { if (globaldir == NULL && pdir != NULL) {
globaldir = vim_strsave(prev_dir); globaldir = vim_strsave(pdir);
} }
} }
@ -7754,10 +7770,13 @@ void post_chdir(CdScope scope, bool trigger_dirchanged)
} }
/// Change directory function used by :cd/:tcd/:lcd Ex commands and the chdir() function. /// 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. /// @return true if the directory is successfully changed.
bool changedir_func(char_u *new_dir, CdScope scope) bool changedir_func(char_u *new_dir, CdScope scope)
{ {
char_u *tofree; char_u *tofree;
char_u *pdir = NULL;
bool retval = false; bool retval = false;
if (new_dir == NULL || allbuf_locked()) { if (new_dir == NULL || allbuf_locked()) {
@ -7766,19 +7785,32 @@ bool changedir_func(char_u *new_dir, CdScope scope)
// ":cd -": Change to previous directory // ":cd -": Change to previous directory
if (STRCMP(new_dir, "-") == 0) { if (STRCMP(new_dir, "-") == 0) {
if (prev_dir == NULL) { pdir = get_prevdir(scope);
if (pdir == NULL) {
EMSG(_("E186: No previous directory")); EMSG(_("E186: No previous directory"));
return false; return false;
} }
new_dir = prev_dir; new_dir = pdir;
} }
// Save current directory for next ":cd -" // Free the previous directory
tofree = prev_dir; tofree = get_prevdir(scope);
if (os_dirname(NameBuff, MAXPATHL) == OK) { if (os_dirname(NameBuff, MAXPATHL) == OK) {
prev_dir = vim_strsave(NameBuff); pdir = vim_strsave(NameBuff);
} else { } else {
prev_dir = NULL; pdir = NULL;
}
switch (scope) {
case kCdScopeTab:
curtab->tp_prevdir = pdir;
break;
case kCdScopeWindow:
curwin->w_prevdir = pdir;
break;
default:
prev_dir = pdir;
} }
#if defined(UNIX) #if defined(UNIX)
@ -7790,12 +7822,12 @@ bool changedir_func(char_u *new_dir, CdScope scope)
} }
#endif #endif
bool dir_differs = prev_dir == NULL || pathcmp((char *)prev_dir, (char *)new_dir, -1) != 0; if (vim_chdir(new_dir) == 0) {
if (dir_differs && vim_chdir(new_dir)) { bool dir_differs = pdir == NULL || pathcmp((char *)pdir, (char *)new_dir, -1) != 0;
EMSG(_(e_failed));
} else {
post_chdir(scope, dir_differs); post_chdir(scope, dir_differs);
retval = true; retval = true;
} else {
EMSG(_(e_failed));
} }
xfree(tofree); xfree(tofree);

View File

@ -54,6 +54,7 @@
#include "nvim/eval.h" #include "nvim/eval.h"
#include "nvim/file_search.h" #include "nvim/file_search.h"
#include "nvim/fileio.h" #include "nvim/fileio.h"
#include "nvim/globals.h"
#include "nvim/memory.h" #include "nvim/memory.h"
#include "nvim/message.h" #include "nvim/message.h"
#include "nvim/misc1.h" #include "nvim/misc1.h"
@ -1666,11 +1667,12 @@ int vim_chdirfile(char_u *fname, CdCause cause)
NameBuff[0] = NUL; NameBuff[0] = NUL;
} }
if (cause != kCdCauseOther && pathcmp(dir, (char *)NameBuff, -1) != 0) { if (os_chdir(dir) == 0) {
if (os_chdir(dir) != 0) { if (cause != kCdCauseOther && pathcmp(dir, (char *)NameBuff, -1) != 0) {
return FAIL; do_autocmd_dirchanged(dir, kCdScopeWindow, cause);
} }
do_autocmd_dirchanged(dir, kCdScopeWindow, cause); } else {
return FAIL;
} }
return OK; return OK;

View File

@ -43,6 +43,20 @@ func Test_cd_minus()
call assert_equal(path_dotdot, getcwd()) call assert_equal(path_dotdot, getcwd())
cd - cd -
call assert_equal(path, getcwd()) 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 endfunc
func Test_cd_with_cpo_chdir() func Test_cd_with_cpo_chdir()
@ -115,6 +129,69 @@ func Test_chdir_func()
call delete('Xdir', 'rf') call delete('Xdir', 'rf')
endfunc 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_cd_from_non_existing_dir() func Test_cd_from_non_existing_dir()
CheckNotMSWindows CheckNotMSWindows

View File

@ -3733,6 +3733,7 @@ void free_tabpage(tabpage_T *tp)
} }
xfree(tp->tp_localdir); xfree(tp->tp_localdir);
xfree(tp->tp_prevdir);
xfree(tp); xfree(tp);
} }
@ -4771,6 +4772,7 @@ static void win_free(win_T *wp, tabpage_T *tp)
} }
xfree(wp->w_localdir); xfree(wp->w_localdir);
xfree(wp->w_prevdir);
/* Remove the window from the b_wininfo lists, it may happen that the /* Remove the window from the b_wininfo lists, it may happen that the
* freed memory is re-used for another window. */ * freed memory is re-used for another window. */