1
0
forked from aniani/vim

patch 8.1.1218: cannot set a directory for a tab page

Problem:    Cannot set a directory for a tab page.
Solution:   Add the tab-local directory. (Yegappan Lakshmanan, closes #4212)
This commit is contained in:
Bram Moolenaar 2019-04-27 20:37:57 +02:00
parent 2155a6abaa
commit 00aa069db8
20 changed files with 441 additions and 66 deletions

View File

@ -1,4 +1,4 @@
*autocmd.txt* For Vim version 8.1. Last change: 2019 Apr 08
*autocmd.txt* For Vim version 8.1. Last change: 2019 Apr 27
VIM REFERENCE MANUAL by Bram Moolenaar
@ -690,13 +690,14 @@ DiffUpdated After diffs have been updated. Depending on
change or when doing |:diffupdate|.
*DirChanged*
DirChanged The working directory has changed in response
to the |:cd| or |:lcd| commands, or as a
result of the 'autochdir' option.
to the |:cd| or |:tcd| or |:lcd| commands, or
as a result of the 'autochdir' option.
The pattern can be:
"window" to trigger on `:lcd`
"global" to trigger on `:cd`
"auto" to trigger on 'autochdir'.
"drop" to trigger on editing a file
"window" to trigger on `:lcd`
"tabpage" to trigger on `:tcd`
"global" to trigger on `:cd`
"auto" to trigger on 'autochdir'.
"drop" to trigger on editing a file
<afile> is set to the new directory name.
*ExitPre*
ExitPre When using `:quit`, `:wq` in a way it makes

View File

@ -1304,9 +1304,10 @@ use has("browsefilter"): >
==============================================================================
7. The current directory *current-directory*
You may use the |:cd| and |:lcd| commands to change to another directory, so
you will not have to type that directory name in front of the file names. It
also makes a difference for executing external commands, e.g. ":!ls".
You can use the |:cd|, |:tcd| and |:lcd| commands to change to another
directory, so you will not have to type that directory name in front of the
file names. It also makes a difference for executing external commands, e.g.
":!ls".
Changing directory fails when the current buffer is modified, the '.' flag is
present in 'cpoptions' and "!" is not used in the command.
@ -1334,6 +1335,17 @@ present in 'cpoptions' and "!" is not used in the command.
*:chd* *:chdir*
:chd[ir][!] [path] Same as |:cd|.
*:tcd*
:tcd[!] {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.
{not in Vi}
*:tch* *:tchdir*
:tch[dir][!] Same as |:tcd|. {not in Vi}
*:lc* *:lcd*
:lc[d][!] {path} Like |:cd|, but only set the current directory when
the cursor is in the current window. The current
@ -1348,17 +1360,26 @@ present in 'cpoptions' and "!" is not used in the command.
:pw[d] Print the current directory name. {Vi: no pwd}
Also see |getcwd()|.
So long as no |:lcd| 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.
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 a |:lcd| command has been used for a window, the specified directory
becomes the current directory for that window. Windows where the |:lcd|
command has not been used stick to the global current directory. When jumping
to another window the current directory will become the last specified local
current directory. If none was specified, the global current directory is
used.
When a |:cd| command is used, the current window will lose his local current
directory and will use the global current directory from now on.
command has not been used stick to the global or tab-local current directory.
When jumping to another window the current directory will become the last
specified local current directory. If none was specified, the global or
tab-local current directory is used.
When a |:tcd| command has been used for a tab page, the specified directory
becomes the current directory for the current tab page and the current window.
The current directory of other tab pages is not affected. When jumping to
another tab page, the current directory will become the last specified local
directory for that tab page. If the current tab has no local current directory
the global current directory is used.
When a |:cd| command is used, the current window and tab page will lose the
local current directory and will use the global current directory from now on.
After using |:cd| the full path name will be used for reading and writing
files. On some networked file systems this may cause problems. The result of

View File

@ -2398,6 +2398,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 the 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
@ -4918,9 +4919,28 @@ getcwd([{winnr} [, {tabnr}]])
directory. See also |haslocaldir()|.
With {winnr} and {tabnr} return the local current directory of
the window in the specified tab page.
the window in the specified tab page. If {winnr} is -1 return
the working directory of the tabpage.
If {winnr} is zero use the current window, if {tabnr} is zero
use the current tabpage.
Without any arguments, return the working directory of the
current window.
Return an empty string if the arguments are invalid.
Examples: >
" Get the working directory of the current window
:echo getcwd()
:echo getcwd(0)
:echo getcwd(0, 0)
" Get the working directory of window 3 in tabpage 2
:echo getcwd(3, 2)
" Get the global working directory
:echo getcwd(-1)
" Get the working directory of tabpage 3
:echo getcwd(-1, 3)
" Get the working directory of current tabpage
:echo getcwd(-1, 0)
<
getfsize({fname}) *getfsize()*
The result is a Number, which is the size in bytes of the
given file {fname}.
@ -5478,16 +5498,39 @@ has_key({dict}, {key}) *has_key()*
an entry with key {key}. Zero otherwise.
haslocaldir([{winnr} [, {tabnr}]]) *haslocaldir()*
The result is a Number, which is 1 when the window has set a
local path via |:lcd|, and 0 otherwise.
The result is a Number:
1 when the window has set a local directory via |:lcd|
2 when the tab-page has set a local directory via |:tcd|
0 otherwise.
Without arguments use the current window.
With {winnr} use this window in the current tab page.
With {winnr} and {tabnr} use the window in the specified tab
page.
{winnr} can be the window number or the |window-ID|.
If {winnr} is -1 it is ignored and only the tabpage is used.
Return 0 if the arguments are invalid.
Examples: >
if haslocaldir() == 1
" window local directory case
elseif haslocaldir() == 2
" tab-local directory case
else
" global directory case
endif
" current window
:echo haslocaldir()
:echo haslocaldir(0)
:echo haslocaldir(0, 0)
" window n in current tab page
:echo haslocaldir(n)
:echo haslocaldir(n, 0)
" window n in tab page m
:echo haslocaldir(n, m)
" tab page m
:echo haslocaldir(-1, m)
<
hasmapto({what} [, {mode} [, {abbr}]]) *hasmapto()*
The result is a Number, which is 1 if there is a mapping that
contains {what} in somewhere in the rhs (what it is mapped to)

View File

@ -1623,6 +1623,8 @@ tag command action ~
|:tab| :tab create new tab when opening new window
|:tag| :ta[g] jump to tag
|:tags| :tags show the contents of the tag stack
|:tcd| :tcd change directory for tab page
|:tchdir| :tch[dir] change directory for tab page
|:tcl| :tc[l] execute Tcl command
|:tcldo| :tcld[o] execute Tcl command for each line
|:tclfile| :tclf[ile] execute Tcl script file

View File

@ -1455,9 +1455,9 @@ A jump table for the options with a short description can be found at |Q_op|.
{not available when compiled without the
|+file_in_path| feature}
This is a list of directories which will be searched when using the
|:cd| and |:lcd| commands, provided that the directory being searched
for has a relative path, not an absolute part starting with "/", "./"
or "../", the 'cdpath' option is not used then.
|:cd|, |:tcd| and |:lcd| commands, provided that the directory being
searched for has a relative path, not an absolute part starting with
"/", "./" or "../", the 'cdpath' option is not used then.
The 'cdpath' option's value has the same form and semantics as
|'path'|. Also see |file-searching|.
The default value is taken from $CDPATH, with a "," prepended to look

View File

@ -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

View File

@ -766,7 +766,7 @@ 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
delete() delete a file

View File

@ -8704,11 +8704,13 @@ find_win_by_nr_or_id(typval_T *vp)
/*
* Find window specified by "wvp" in tabpage "tvp".
* Returns the tab page in 'ptp'
*/
win_T *
find_tabwin(
typval_T *wvp, /* VAR_UNKNOWN for current window */
typval_T *tvp) /* VAR_UNKNOWN for current tab page */
typval_T *wvp, // VAR_UNKNOWN for current window
typval_T *tvp, // VAR_UNKNOWN for current tab page
tabpage_T **ptp)
{
win_T *wp = NULL;
tabpage_T *tp = NULL;
@ -8726,10 +8728,22 @@ find_tabwin(
tp = curtab;
if (tp != NULL)
{
wp = find_win_by_nr(wvp, tp);
if (wp == NULL && wvp->v_type == VAR_NUMBER
&& wvp->vval.v_number != -1)
// A window with the specified number is not found
tp = NULL;
}
}
else
{
wp = curwin;
tp = curtab;
}
if (ptp != NULL)
*ptp = tp;
return wp;
}

View File

@ -1529,7 +1529,7 @@ f_arglistid(typval_T *argvars, typval_T *rettv)
win_T *wp;
rettv->vval.v_number = -1;
wp = find_tabwin(&argvars[0], &argvars[1]);
wp = find_tabwin(&argvars[0], &argvars[1], NULL);
if (wp != NULL)
rettv->vval.v_number = wp->w_alist->id;
}
@ -5126,25 +5126,44 @@ f_getcompletion(typval_T *argvars, typval_T *rettv)
/*
* "getcwd()" function
*
* Return the current working directory of a window in a tab page.
* First optional argument 'winnr' is the window number or -1 and the second
* optional argument 'tabnr' is the tab page number.
*
* If no arguments are supplied, then return the directory of the current
* window.
* If only 'winnr' is specified and is not -1 or 0 then return the directory of
* the specified window.
* If 'winnr' is 0 then return the directory of the current window.
* If both 'winnr and 'tabnr' are specified and 'winnr' is -1 then return the
* directory of the specified tab page. Otherwise return the directory of the
* specified window in the specified tab page.
* If the window or the tab page doesn't exist then return NULL.
*/
static void
f_getcwd(typval_T *argvars, typval_T *rettv)
{
win_T *wp = NULL;
tabpage_T *tp = NULL;
char_u *cwd;
int global = FALSE;
rettv->v_type = VAR_STRING;
rettv->vval.v_string = NULL;
if (argvars[0].v_type == VAR_NUMBER && argvars[0].vval.v_number == -1)
if (argvars[0].v_type == VAR_NUMBER
&& argvars[0].vval.v_number == -1
&& argvars[1].v_type == VAR_UNKNOWN)
global = TRUE;
else
wp = find_tabwin(&argvars[0], &argvars[1]);
wp = find_tabwin(&argvars[0], &argvars[1], &tp);
if (wp != NULL && wp->w_localdir != NULL)
rettv->vval.v_string = vim_strsave(wp->w_localdir);
else if (wp != NULL || global)
else if (tp != NULL && tp->tp_localdir != NULL)
rettv->vval.v_string = vim_strsave(tp->tp_localdir);
else if (wp != NULL || tp != NULL || global)
{
if (globaldir != NULL)
rettv->vval.v_string = vim_strsave(globaldir);
@ -5333,7 +5352,7 @@ f_getjumplist(typval_T *argvars, typval_T *rettv)
return;
#ifdef FEAT_JUMPLIST
wp = find_tabwin(&argvars[0], &argvars[1]);
wp = find_tabwin(&argvars[0], &argvars[1], NULL);
if (wp == NULL)
return;
@ -6824,10 +6843,18 @@ f_has_key(typval_T *argvars, typval_T *rettv)
static void
f_haslocaldir(typval_T *argvars, typval_T *rettv)
{
tabpage_T *tp = NULL;
win_T *wp = NULL;
wp = find_tabwin(&argvars[0], &argvars[1]);
rettv->vval.v_number = (wp != NULL && wp->w_localdir != NULL);
wp = find_tabwin(&argvars[0], &argvars[1], &tp);
// Check for window-local and tab-local directories
if (wp != NULL && wp->w_localdir != NULL)
rettv->vval.v_number = 1;
else if (tp != NULL && tp->tp_localdir != NULL)
rettv->vval.v_number = 2;
else
rettv->vval.v_number = 0;
}
/*

View File

@ -25,12 +25,12 @@ static const unsigned short cmdidxs1[26] =
/* r */ 351,
/* s */ 371,
/* t */ 439,
/* u */ 482,
/* v */ 493,
/* w */ 511,
/* x */ 525,
/* y */ 534,
/* z */ 535
/* u */ 484,
/* v */ 495,
/* w */ 513,
/* x */ 527,
/* y */ 536,
/* z */ 537
};
/*
@ -60,7 +60,7 @@ static const unsigned char cmdidxs2[26][26] =
/* q */ { 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
/* r */ { 0, 0, 0, 0, 0, 0, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 19, 0, 0, 0, 0 },
/* s */ { 2, 6, 15, 0, 19, 23, 0, 25, 26, 0, 0, 29, 31, 35, 39, 41, 0, 49, 0, 50, 0, 62, 63, 0, 64, 0 },
/* t */ { 2, 0, 19, 0, 22, 24, 0, 25, 0, 26, 0, 27, 31, 34, 36, 37, 0, 38, 40, 0, 41, 0, 0, 0, 0, 0 },
/* t */ { 2, 0, 19, 0, 24, 26, 0, 27, 0, 28, 0, 29, 33, 36, 38, 39, 0, 40, 42, 0, 43, 0, 0, 0, 0, 0 },
/* u */ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
/* v */ { 0, 0, 0, 0, 1, 0, 0, 0, 4, 0, 0, 0, 9, 12, 0, 0, 0, 0, 15, 0, 16, 0, 0, 0, 0, 0 },
/* w */ { 2, 0, 0, 0, 0, 0, 0, 3, 4, 0, 0, 0, 0, 8, 0, 9, 10, 0, 0, 0, 12, 13, 0, 0, 0, 0 },
@ -69,4 +69,4 @@ static const unsigned char cmdidxs2[26][26] =
/* z */ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
};
static const int command_count = 548;
static const int command_count = 550;

View File

@ -1479,6 +1479,12 @@ EX(CMD_tabrewind, "tabrewind", ex_tabnext,
EX(CMD_tabs, "tabs", ex_tabs,
TRLBAR|CMDWIN,
ADDR_TABS),
EX(CMD_tcd, "tcd", ex_cd,
BANG|FILE1|TRLBAR|CMDWIN,
ADDR_OTHER),
EX(CMD_tchdir, "tchdir", ex_cd,
BANG|FILE1|TRLBAR|CMDWIN,
ADDR_OTHER),
EX(CMD_tcl, "tcl", ex_tcl,
RANGE|EXTRA|NEEDARG|CMDWIN|RESTRICT,
ADDR_LINES),

View File

@ -3692,6 +3692,8 @@ set_one_cmd_context(
break;
case CMD_cd:
case CMD_chdir:
case CMD_tcd:
case CMD_tchdir:
case CMD_lcd:
case CMD_lchdir:
if (xp->xp_context == EXPAND_FILES)
@ -7435,13 +7437,17 @@ free_cd_dir(void)
/*
* Deal with the side effects of changing the current directory.
* When "local" is TRUE then this was after an ":lcd" command.
* When "tablocal" is TRUE then this was after an ":tcd" command.
* When "winlocal" is TRUE then this was after an ":lcd" command.
*/
void
post_chdir(int local)
post_chdir(int tablocal, int winlocal)
{
if (!winlocal)
// Clear tab local directory for both :cd and :tcd
VIM_CLEAR(curtab->tp_localdir);
VIM_CLEAR(curwin->w_localdir);
if (local)
if (winlocal || tablocal)
{
/* If still in global directory, need to remember current
* directory as global directory. */
@ -7449,7 +7455,12 @@ post_chdir(int local)
globaldir = vim_strsave(prev_dir);
/* Remember this local directory for the window. */
if (mch_dirname(NameBuff, MAXPATHL) == OK)
curwin->w_localdir = vim_strsave(NameBuff);
{
if (tablocal)
curtab->tp_localdir = vim_strsave(NameBuff);
else
curwin->w_localdir = vim_strsave(NameBuff);
}
}
else
{
@ -7463,7 +7474,7 @@ post_chdir(int local)
/*
* ":cd", ":lcd", ":chdir" and ":lchdir".
* ":cd", ":tcd", ":lcd", ":chdir" ":tchdir" and ":lchdir".
*/
void
ex_cd(exarg_T *eap)
@ -7532,19 +7543,29 @@ ex_cd(exarg_T *eap)
emsg(_(e_failed));
else
{
int is_local_chdir = eap->cmdidx == CMD_lcd
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_local_chdir);
post_chdir(is_tablocal_chdir, is_winlocal_chdir);
/* Echo the new current directory if the command was typed. */
if (KeyTyped || p_verbose >= 5)
ex_pwd(eap);
if (dir_differs)
apply_autocmds(EVENT_DIRCHANGED,
is_local_chdir ? (char_u *)"window" : (char_u *)"global",
{
if (is_winlocal_chdir)
acmd_fname = (char_u *)"window";
else if (is_tablocal_chdir)
acmd_fname = (char_u *)"tabpage";
else
acmd_fname = (char_u *)"global";
apply_autocmds(EVENT_DIRCHANGED, acmd_fname,
new_dir, FALSE, curbuf);
}
}
vim_free(tofree);
}
@ -9729,12 +9750,13 @@ makeopens(
}
for (tabnr = 1; ; ++tabnr)
{
tabpage_T *tp = NULL;
int need_tabnext = FALSE;
int cnr = 1;
if ((ssop_flags & SSOP_TABPAGES))
{
tabpage_T *tp = find_tabpage(tabnr);
tp = find_tabpage(tabnr);
if (tp == NULL)
break; /* done all tab pages */
@ -9833,6 +9855,18 @@ makeopens(
if (nr > 1 && ses_winsizes(fd, restore_size, tab_firstwin) == FAIL)
return FAIL;
// Restore the tab-local working directory if specified
// Do this before the windows, so that the window-local directory can
// override the tab-local directory.
if (tp != NULL && tp->tp_localdir != NULL && ssop_flags & SSOP_CURDIR)
{
if (fputs("tcd ", fd) < 0
|| ses_put_fname(fd, tp->tp_localdir, &ssop_flags) == FAIL
|| put_eol(fd) == FAIL)
return FAIL;
did_lcd = TRUE;
}
/*
* Restore the view of the window (options, file, cursor, etc.).
*/

View File

@ -1032,7 +1032,7 @@ _VimChdir(PyObject *_chdir, PyObject *args, PyObject *kwargs)
Py_DECREF(newwd);
Py_XDECREF(todecref);
post_chdir(FALSE);
post_chdir(FALSE, FALSE);
if (VimTryEnd())
{

View File

@ -116,7 +116,7 @@ void ex_echohl(exarg_T *eap);
void ex_execute(exarg_T *eap);
win_T *find_win_by_nr(typval_T *vp, tabpage_T *tp);
win_T *find_win_by_nr_or_id(typval_T *vp);
win_T *find_tabwin(typval_T *wvp, typval_T *tvp);
win_T *find_tabwin(typval_T *wvp, typval_T *tvp, tabpage_T **ptp);
void getwinvar(typval_T *argvars, typval_T *rettv, int off);
void setwinvar(typval_T *argvars, typval_T *rettv, int off);
char_u *autoload_name(char_u *name);

View File

@ -37,7 +37,7 @@ void ex_splitview(exarg_T *eap);
void tabpage_new(void);
void do_exedit(exarg_T *eap, win_T *old_curwin);
void free_cd_dir(void);
void post_chdir(int local);
void post_chdir(int tablocal, int winlocal);
void ex_cd(exarg_T *eap);
void do_sleep(long msec);
void ex_may_print(exarg_T *eap);

View File

@ -2574,6 +2574,9 @@ struct tabpage_S
int tp_prev_which_scrollbars[3];
/* previous value of which_scrollbars */
#endif
char_u *tp_localdir; // absolute path of local directory or
// NULL
#ifdef FEAT_DIFF
diff_T *tp_first_diff;
buf_T *(tp_diffbuf[DB_COUNT]);

View File

@ -97,6 +97,17 @@ function Test_GetCwd()
call assert_equal("y Xdir2 1", GetCwdInfo(2, tp_nr))
call assert_equal("z Xdir3 1", GetCwdInfo(1, tp_nr))
call assert_equal(g:topdir, getcwd(-1))
" Non existing windows and tab pages
call assert_equal('', getcwd(100))
call assert_equal(0, haslocaldir(100))
call assert_equal('', getcwd(10, 1))
call assert_equal(0, haslocaldir(10, 1))
call assert_equal('', getcwd(1, 5))
call assert_equal(0, haslocaldir(1, 5))
call assert_fails('call getcwd([])', 'E745:')
call assert_fails('call getcwd(1, [])', 'E745:')
call assert_fails('call haslocaldir([])', 'E745:')
call assert_fails('call haslocaldir(1, [])', 'E745:')
endfunc
function Test_GetCwd_lcd_shellslash()
@ -110,3 +121,144 @@ function Test_GetCwd_lcd_shellslash()
call assert_equal(cwd[-1:], '\')
endif
endfunc
" Test for :tcd
function Test_Tab_Local_Cwd()
enew | only | tabonly
call mkdir('Xtabdir1')
call mkdir('Xtabdir2')
call mkdir('Xwindir1')
call mkdir('Xwindir2')
call mkdir('Xwindir3')
" Create three tabpages with three windows each
edit a
botright new b
botright new c
tabnew m
botright new n
botright new o
tabnew x
botright new y
botright new z
" Setup different directories for the tab pages and windows
tabrewind
1wincmd w
lcd Xwindir1
tabnext
tcd Xtabdir1
2wincmd w
lcd ../Xwindir2
tabnext
tcd Xtabdir2
3wincmd w
lcd ../Xwindir3
" Check the directories of various windows
call assert_equal("a Xwindir1 1", GetCwdInfo(1, 1))
call assert_equal("b Xtopdir 0", GetCwdInfo(2, 1))
call assert_equal("c Xtopdir 0", GetCwdInfo(3, 1))
call assert_equal("m Xtabdir1 2", GetCwdInfo(1, 2))
call assert_equal("n Xwindir2 1", GetCwdInfo(2, 2))
call assert_equal("o Xtabdir1 2", GetCwdInfo(3, 2))
call assert_equal("x Xtabdir2 2", GetCwdInfo(1, 3))
call assert_equal("y Xtabdir2 2", GetCwdInfo(2, 3))
call assert_equal("z Xwindir3 1", GetCwdInfo(3, 3))
" Check the tabpage directories
call assert_equal('Xtopdir', fnamemodify(getcwd(-1, 1), ':t'))
call assert_equal('Xtabdir1', fnamemodify(getcwd(-1, 2), ':t'))
call assert_equal('Xtabdir2', fnamemodify(getcwd(-1, 3), ':t'))
call assert_equal('', fnamemodify(getcwd(-1, 4), ':t'))
" Jump to different windows in the tab pages and check the current directory
tabrewind | 1wincmd w
call assert_equal('Xwindir1', fnamemodify(getcwd(), ':t'))
call assert_equal('Xwindir1', fnamemodify(getcwd(0), ':t'))
call assert_equal('Xwindir1', fnamemodify(getcwd(0, 0), ':t'))
call assert_true(haslocaldir(0))
call assert_equal(0, haslocaldir(-1, 0))
call assert_equal('Xtopdir', fnamemodify(getcwd(-1, 0), ':t'))
call assert_equal(g:topdir, getcwd(-1))
2wincmd w
call assert_equal('Xtopdir', fnamemodify(getcwd(), ':t'))
call assert_equal('Xtopdir', fnamemodify(getcwd(0), ':t'))
call assert_equal('Xtopdir', fnamemodify(getcwd(0, 0), ':t'))
call assert_false(haslocaldir(0))
call assert_equal(0, haslocaldir(-1, 0))
call assert_equal('Xtopdir', fnamemodify(getcwd(-1, 0), ':t'))
call assert_equal(g:topdir, getcwd(-1))
tabnext | 1wincmd w
call assert_equal('Xtabdir1', fnamemodify(getcwd(), ':t'))
call assert_equal('Xtabdir1', fnamemodify(getcwd(0), ':t'))
call assert_equal('Xtabdir1', fnamemodify(getcwd(0, 0), ':t'))
call assert_true(haslocaldir(0))
call assert_equal(2, haslocaldir(-1, 0))
call assert_equal('Xtabdir1', fnamemodify(getcwd(-1, 0), ':t'))
call assert_equal(g:topdir, getcwd(-1))
2wincmd w
call assert_equal('Xwindir2', fnamemodify(getcwd(), ':t'))
call assert_equal('Xwindir2', fnamemodify(getcwd(0), ':t'))
call assert_equal('Xwindir2', fnamemodify(getcwd(0, 0), ':t'))
call assert_true(haslocaldir(0))
call assert_equal(2, haslocaldir(-1, 0))
call assert_equal('Xtabdir1', fnamemodify(getcwd(-1, 0), ':t'))
call assert_equal(g:topdir, getcwd(-1))
tabnext | 1wincmd w
call assert_equal('Xtabdir2', fnamemodify(getcwd(), ':t'))
call assert_equal('Xtabdir2', fnamemodify(getcwd(0), ':t'))
call assert_equal('Xtabdir2', fnamemodify(getcwd(0, 0), ':t'))
call assert_true(haslocaldir(0))
call assert_equal(2, haslocaldir(-1, 0))
call assert_equal('Xtabdir2', fnamemodify(getcwd(-1, 0), ':t'))
call assert_equal(g:topdir, getcwd(-1))
3wincmd w
call assert_equal('Xwindir3', fnamemodify(getcwd(), ':t'))
call assert_equal('Xwindir3', fnamemodify(getcwd(0), ':t'))
call assert_equal('Xwindir3', fnamemodify(getcwd(0, 0), ':t'))
call assert_true(haslocaldir(0))
call assert_equal(2, haslocaldir(-1, 0))
call assert_equal('Xtabdir2', fnamemodify(getcwd(-1, 0), ':t'))
call assert_equal(g:topdir, getcwd(-1))
" A new tab page should inherit the directory of the current tab page
tabrewind | 1wincmd w
tabnew g
call assert_equal("g Xwindir1 1", GetCwdInfo(0, 0))
tabclose | tabrewind
2wincmd w
tabnew h
call assert_equal("h Xtopdir 0", GetCwdInfo(0, 0))
tabclose
tabnext 2 | 1wincmd w
tabnew j
call assert_equal("j Xtabdir1 2", GetCwdInfo(0, 0))
tabclose
" Change the global directory for the first tab page
tabrewind | 1wincmd w
cd ../Xdir1
call assert_equal("a Xdir1 0", GetCwdInfo(1, 1))
call assert_equal("b Xdir1 0", GetCwdInfo(2, 1))
call assert_equal("m Xtabdir1 2", GetCwdInfo(1, 2))
call assert_equal("n Xwindir2 1", GetCwdInfo(2, 2))
" Change the global directory for the second tab page
tabnext | 1wincmd w
cd ../Xdir3
call assert_equal("m Xdir3 0", GetCwdInfo(1, 2))
call assert_equal("n Xwindir2 1", GetCwdInfo(2, 2))
call assert_equal("o Xdir3 0", GetCwdInfo(3, 2))
" Change the tab-local directory for the third tab page
tabnext | 1wincmd w
cd ../Xdir1
call assert_equal("x Xdir1 0", GetCwdInfo(1, 3))
call assert_equal("y Xdir1 0", GetCwdInfo(2, 3))
call assert_equal("z Xwindir3 1", GetCwdInfo(3, 3))
enew | only | tabonly
new
endfunc

View File

@ -210,6 +210,48 @@ func Test_mksession_lcd_multiple_tabs()
call delete('Xtest_mks.out')
endfunc
" Test for tabpage-local directory
func Test_mksession_tcd_multiple_tabs()
let save_cwd = getcwd()
call mkdir('Xtopdir')
cd Xtopdir
call mkdir('Xtabdir1')
call mkdir('Xtabdir2')
call mkdir('Xtabdir3')
call mkdir('Xwindir1')
call mkdir('Xwindir2')
call mkdir('Xwindir3')
tcd Xtabdir1
botright new
wincmd t
lcd ../Xwindir1
tabnew
tcd ../Xtabdir2
botright new
lcd ../Xwindir2
tabnew
tcd ../Xtabdir3
botright new
lcd ../Xwindir3
tabfirst
1wincmd w
mksession! Xtest_mks.out
only | tabonly
source Xtest_mks.out
call assert_equal('Xtabdir1', fnamemodify(getcwd(-1, 1), ':t'))
call assert_equal('Xwindir1', fnamemodify(getcwd(1, 1), ':t'))
call assert_equal('Xtabdir1', fnamemodify(getcwd(2, 1), ':t'))
call assert_equal('Xtabdir2', fnamemodify(getcwd(-1, 2), ':t'))
call assert_equal('Xtabdir2', fnamemodify(getcwd(1, 2), ':t'))
call assert_equal('Xwindir2', fnamemodify(getcwd(2, 2), ':t'))
call assert_equal('Xtabdir3', fnamemodify(getcwd(-1, 3), ':t'))
call assert_equal('Xtabdir3', fnamemodify(getcwd(1, 3), ':t'))
call assert_equal('Xwindir3', fnamemodify(getcwd(2, 3), ':t'))
only | tabonly
exe 'cd ' . save_cwd
call delete("Xtopdir", "rf")
endfunc
func Test_mksession_blank_tabs()
tabnew
tabnew

View File

@ -767,6 +767,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
1218,
/**/
1217,
/**/

View File

@ -3625,6 +3625,8 @@ free_tabpage(tabpage_T *tp)
unref_var_dict(tp->tp_vars);
#endif
vim_free(tp->tp_localdir);
#ifdef FEAT_PYTHON
python_tabpage_free(tp);
#endif
@ -3662,6 +3664,8 @@ win_new_tabpage(int after)
}
curtab = newtp;
newtp->tp_localdir = (tp->tp_localdir == NULL)
? NULL : vim_strsave(tp->tp_localdir);
/* Create a new empty window. */
if (win_alloc_firstwin(tp->tp_curwin) == OK)
{
@ -3839,6 +3843,9 @@ find_tabpage(int n)
tabpage_T *tp;
int i = 1;
if (n == 0)
return curtab;
for (tp = first_tabpage; tp != NULL && i != n; tp = tp->tp_next)
++i;
return tp;
@ -4451,11 +4458,13 @@ win_enter_ext(
curwin->w_cursor.coladd = 0;
changed_line_abv_curs(); /* assume cursor position needs updating */
if (curwin->w_localdir != NULL)
if (curwin->w_localdir != NULL || curtab->tp_localdir != NULL)
{
/* Window has a local directory: Save current directory as global
* directory (unless that was done already) and change to the local
* directory. */
char_u *dirname;
// Window or tab has a local directory: Save current directory as
// global directory (unless that was done already) and change to the
// local directory.
if (globaldir == NULL)
{
char_u cwd[MAXPATHL];
@ -4463,7 +4472,12 @@ win_enter_ext(
if (mch_dirname(cwd, MAXPATHL) == OK)
globaldir = vim_strsave(cwd);
}
if (mch_chdir((char *)curwin->w_localdir) == 0)
if (curwin->w_localdir != NULL)
dirname = curwin->w_localdir;
else
dirname = curtab->tp_localdir;
if (mch_chdir((char *)dirname) == 0)
shorten_fnames(TRUE);
}
else if (globaldir != NULL)