To: vim_dev@googlegroups.com Subject: Patch 8.1.1218 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.1.1218 Problem: Cannot set a directory for a tab page. Solution: Add the tab-local directory. (Yegappan Lakshmanan, closes #4212) Files: runtime/doc/autocmd.txt, runtime/doc/editing.txt, runtime/doc/eval.txt, runtime/doc/index.txt, runtime/doc/options.txt, runtime/doc/usr_22.txt, runtime/doc/usr_41.txt, src/eval.c, src/evalfunc.c, src/ex_cmdidxs.h, src/ex_cmds.h, src/ex_docmd.c, src/if_py_both.h, src/proto/eval.pro, src/proto/ex_docmd.pro, src/structs.h, src/testdir/test_getcwd.vim, src/testdir/test_mksession.vim, src/window.c *** ../vim-8.1.1217/runtime/doc/autocmd.txt 2019-04-08 18:15:36.464223229 +0200 --- runtime/doc/autocmd.txt 2019-04-27 20:06:26.758264881 +0200 *************** *** 686,704 **** 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. 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 is set to the new directory name. *ExitPre* ExitPre When using `:quit`, `:wq` in a way it makes Vim exit, or using `:qall`, just after |QuitPre|. Can be used to close any ! non-essential window. *FileChangedShell* FileChangedShell When Vim notices that the modification time of a file has changed since editing started. --- 690,712 ---- change or when doing |:diffupdate|. *DirChanged* DirChanged The working directory has changed in response ! 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` ! "tabpage" to trigger on `:tcd` ! "global" to trigger on `:cd` ! "auto" to trigger on 'autochdir'. ! "drop" to trigger on editing a file is set to the new directory name. *ExitPre* ExitPre When using `:quit`, `:wq` in a way it makes Vim exit, or using `:qall`, just after |QuitPre|. Can be used to close any ! non-essential window. Exiting may still be ! cancelled if there is a modified buffer that ! isn't automatically saved, use |VimLeavePre| ! for really exiting. *FileChangedShell* FileChangedShell When Vim notices that the modification time of a file has changed since editing started. *** ../vim-8.1.1217/runtime/doc/editing.txt 2018-05-17 13:40:51.000000000 +0200 --- runtime/doc/editing.txt 2019-04-27 20:11:37.192857657 +0200 *************** *** 1304,1312 **** ============================================================================== 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". Changing directory fails when the current buffer is modified, the '.' flag is present in 'cpoptions' and "!" is not used in the command. --- 1304,1313 ---- ============================================================================== 7. The current directory *current-directory* ! 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,1339 **** --- 1335,1351 ---- *: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,1364 **** :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. 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. 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 --- 1360,1385 ---- :pw[d] Print the current directory name. {Vi: no pwd} Also see |getcwd()|. ! 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 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 *** ../vim-8.1.1217/runtime/doc/eval.txt 2019-04-27 13:03:20.012715914 +0200 --- runtime/doc/eval.txt 2019-04-27 20:14:41.136007351 +0200 *************** *** 2377,2382 **** --- 2398,2404 ---- 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 *************** *** 4880,4886 **** getcwd([{winnr} [, {tabnr}]]) The result is a String, which is the name of the current working directory. - Without arguments, for the current window. With {winnr} return the local current directory of this window in the current tab page. {winnr} can be the window number or --- 4911,4916 ---- *************** *** 4889,4897 **** directory. See also |haslocaldir()|. With {winnr} and {tabnr} return the local current directory of ! the window in the specified tab page. Return an empty string if the arguments are invalid. getfsize({fname}) *getfsize()* The result is a Number, which is the size in bytes of the given file {fname}. --- 4919,4946 ---- directory. See also |haslocaldir()|. With {winnr} and {tabnr} return the local current directory of ! 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}. *************** *** 5449,5464 **** 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. 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|. Return 0 if the arguments are invalid. 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) --- 5498,5536 ---- an entry with key {key}. Zero otherwise. haslocaldir([{winnr} [, {tabnr}]]) *haslocaldir()* ! 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) *** ../vim-8.1.1217/runtime/doc/index.txt 2018-10-19 22:35:04.885189994 +0200 --- runtime/doc/index.txt 2019-04-27 20:05:21.278555302 +0200 *************** *** 1573,1578 **** --- 1627,1634 ---- |: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 *** ../vim-8.1.1217/runtime/doc/options.txt 2019-02-16 15:09:21.225946157 +0100 --- runtime/doc/options.txt 2019-04-27 20:05:21.278555302 +0200 *************** *** 1457,1465 **** {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. 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 --- 1455,1463 ---- {not available when compiled without the |+file_in_path| feature} This is a list of directories which will be searched when using the ! |: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 *** ../vim-8.1.1217/runtime/doc/usr_22.txt 2018-05-17 13:42:03.000000000 +0200 --- runtime/doc/usr_22.txt 2019-04-27 20:17:02.375349029 +0200 *************** *** 202,215 **** :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 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 directory, it will go back to using the shared directory. ============================================================================== *22.3* Finding a file --- 202,229 ---- :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 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 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 *** ../vim-8.1.1217/runtime/doc/usr_41.txt 2019-04-06 13:18:06.733335092 +0200 --- runtime/doc/usr_41.txt 2019-04-27 20:05:21.278555302 +0200 *************** *** 765,771 **** 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| tempname() get the name of a temporary file mkdir() create a new directory delete() delete a file --- 766,772 ---- 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| or |:tcd| tempname() get the name of a temporary file mkdir() create a new directory delete() delete a file *** ../vim-8.1.1217/src/eval.c 2019-04-27 13:03:20.004715961 +0200 --- src/eval.c 2019-04-27 20:17:32.523208020 +0200 *************** *** 8704,8714 **** /* * Find window specified by "wvp" in tabpage "tvp". */ win_T * find_tabwin( ! typval_T *wvp, /* VAR_UNKNOWN for current window */ ! typval_T *tvp) /* VAR_UNKNOWN for current tab page */ { win_T *wp = NULL; tabpage_T *tp = NULL; --- 8704,8716 ---- /* * 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 ! tabpage_T **ptp) { win_T *wp = NULL; tabpage_T *tp = NULL; *************** *** 8726,8735 **** --- 8728,8749 ---- 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; } *** ../vim-8.1.1217/src/evalfunc.c 2019-04-27 13:03:20.004715961 +0200 --- src/evalfunc.c 2019-04-27 20:05:21.282555283 +0200 *************** *** 1529,1535 **** win_T *wp; rettv->vval.v_number = -1; ! wp = find_tabwin(&argvars[0], &argvars[1]); if (wp != NULL) rettv->vval.v_number = wp->w_alist->id; } --- 1529,1535 ---- win_T *wp; rettv->vval.v_number = -1; ! wp = find_tabwin(&argvars[0], &argvars[1], NULL); if (wp != NULL) rettv->vval.v_number = wp->w_alist->id; } *************** *** 5126,5150 **** /* * "getcwd()" function */ static void f_getcwd(typval_T *argvars, typval_T *rettv) { win_T *wp = 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) global = TRUE; else ! wp = find_tabwin(&argvars[0], &argvars[1]); if (wp != NULL && wp->w_localdir != NULL) rettv->vval.v_string = vim_strsave(wp->w_localdir); ! else if (wp != NULL || global) { if (globaldir != NULL) rettv->vval.v_string = vim_strsave(globaldir); --- 5126,5169 ---- /* * "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 ! && argvars[1].v_type == VAR_UNKNOWN) global = TRUE; else ! 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 (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,5339 **** return; #ifdef FEAT_JUMPLIST ! wp = find_tabwin(&argvars[0], &argvars[1]); if (wp == NULL) return; --- 5352,5358 ---- return; #ifdef FEAT_JUMPLIST ! wp = find_tabwin(&argvars[0], &argvars[1], NULL); if (wp == NULL) return; *************** *** 6824,6833 **** static void f_haslocaldir(typval_T *argvars, typval_T *rettv) { win_T *wp = NULL; ! wp = find_tabwin(&argvars[0], &argvars[1]); ! rettv->vval.v_number = (wp != NULL && wp->w_localdir != NULL); } /* --- 6843,6860 ---- 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], &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; } /* *** ../vim-8.1.1217/src/ex_cmdidxs.h 2019-04-04 18:15:05.770857065 +0200 --- src/ex_cmdidxs.h 2019-04-27 20:05:21.282555283 +0200 *************** *** 25,36 **** /* r */ 351, /* s */ 371, /* t */ 439, ! /* u */ 482, ! /* v */ 493, ! /* w */ 511, ! /* x */ 525, ! /* y */ 534, ! /* z */ 535 }; /* --- 25,36 ---- /* r */ 351, /* s */ 371, /* t */ 439, ! /* u */ 484, ! /* v */ 495, ! /* w */ 513, ! /* x */ 527, ! /* y */ 536, ! /* z */ 537 }; /* *************** *** 60,66 **** /* 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 }, /* 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 }, --- 60,66 ---- /* 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, 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,72 **** /* 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; --- 69,72 ---- /* 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 = 550; *** ../vim-8.1.1217/src/ex_cmds.h 2019-04-27 13:03:20.004715961 +0200 --- src/ex_cmds.h 2019-04-27 20:18:38.294899911 +0200 *************** *** 1479,1484 **** --- 1479,1490 ---- 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), *** ../vim-8.1.1217/src/ex_docmd.c 2019-04-27 13:03:20.004715961 +0200 --- src/ex_docmd.c 2019-04-27 20:05:21.282555283 +0200 *************** *** 3692,3697 **** --- 3692,3699 ---- 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,7447 **** /* * Deal with the side effects of changing the current directory. ! * When "local" is TRUE then this was after an ":lcd" command. */ void ! post_chdir(int local) { VIM_CLEAR(curwin->w_localdir); ! if (local) { /* If still in global directory, need to remember current * directory as global directory. */ --- 7437,7453 ---- /* * Deal with the side effects of changing the current directory. ! * 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 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 (winlocal || tablocal) { /* If still in global directory, need to remember current * directory as global directory. */ *************** *** 7449,7455 **** globaldir = vim_strsave(prev_dir); /* Remember this local directory for the window. */ if (mch_dirname(NameBuff, MAXPATHL) == OK) ! curwin->w_localdir = vim_strsave(NameBuff); } else { --- 7455,7466 ---- globaldir = vim_strsave(prev_dir); /* Remember this local directory for the window. */ if (mch_dirname(NameBuff, MAXPATHL) == OK) ! { ! if (tablocal) ! curtab->tp_localdir = vim_strsave(NameBuff); ! else ! curwin->w_localdir = vim_strsave(NameBuff); ! } } else { *************** *** 7463,7469 **** /* ! * ":cd", ":lcd", ":chdir" and ":lchdir". */ void ex_cd(exarg_T *eap) --- 7474,7480 ---- /* ! * ":cd", ":tcd", ":lcd", ":chdir" ":tchdir" and ":lchdir". */ void ex_cd(exarg_T *eap) *************** *** 7532,7550 **** emsg(_(e_failed)); else { ! int is_local_chdir = eap->cmdidx == CMD_lcd || eap->cmdidx == CMD_lchdir; ! post_chdir(is_local_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", new_dir, FALSE, curbuf); } vim_free(tofree); } --- 7543,7571 ---- emsg(_(e_failed)); else { ! 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); /* Echo the new current directory if the command was typed. */ if (KeyTyped || p_verbose >= 5) ex_pwd(eap); if (dir_differs) ! { ! 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,9740 **** } for (tabnr = 1; ; ++tabnr) { int need_tabnext = FALSE; int cnr = 1; if ((ssop_flags & SSOP_TABPAGES)) { ! tabpage_T *tp = find_tabpage(tabnr); if (tp == NULL) break; /* done all tab pages */ --- 9750,9762 ---- } for (tabnr = 1; ; ++tabnr) { + tabpage_T *tp = NULL; int need_tabnext = FALSE; int cnr = 1; if ((ssop_flags & SSOP_TABPAGES)) { ! tp = find_tabpage(tabnr); if (tp == NULL) break; /* done all tab pages */ *************** *** 9833,9838 **** --- 9855,9872 ---- 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.). */ *** ../vim-8.1.1217/src/if_py_both.h 2019-03-30 12:51:18.626808012 +0100 --- src/if_py_both.h 2019-04-27 20:05:21.282555283 +0200 *************** *** 1032,1038 **** Py_DECREF(newwd); Py_XDECREF(todecref); ! post_chdir(FALSE); if (VimTryEnd()) { --- 1032,1038 ---- Py_DECREF(newwd); Py_XDECREF(todecref); ! post_chdir(FALSE, FALSE); if (VimTryEnd()) { *** ../vim-8.1.1217/src/proto/eval.pro 2019-04-05 22:50:35.025737353 +0200 --- src/proto/eval.pro 2019-04-27 20:05:21.286555269 +0200 *************** *** 116,122 **** 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); 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); --- 116,122 ---- 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, 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); *** ../vim-8.1.1217/src/proto/ex_docmd.pro 2019-04-27 13:03:20.004715961 +0200 --- src/proto/ex_docmd.pro 2019-04-27 20:05:21.286555269 +0200 *************** *** 37,43 **** 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 ex_cd(exarg_T *eap); void do_sleep(long msec); void ex_may_print(exarg_T *eap); --- 37,43 ---- void tabpage_new(void); void do_exedit(exarg_T *eap, win_T *old_curwin); void free_cd_dir(void); ! 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); *** ../vim-8.1.1217/src/structs.h 2019-04-27 13:03:20.008715938 +0200 --- src/structs.h 2019-04-27 20:05:21.286555269 +0200 *************** *** 2574,2579 **** --- 2574,2582 ---- 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]); *** ../vim-8.1.1217/src/testdir/test_getcwd.vim 2019-01-09 23:00:57.997176121 +0100 --- src/testdir/test_getcwd.vim 2019-04-27 20:05:21.286555269 +0200 *************** *** 97,102 **** --- 97,113 ---- 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,112 **** --- 121,264 ---- 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 *** ../vim-8.1.1217/src/testdir/test_mksession.vim 2019-02-03 14:52:42.505867463 +0100 --- src/testdir/test_mksession.vim 2019-04-27 20:05:21.286555269 +0200 *************** *** 210,215 **** --- 210,257 ---- 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 *** ../vim-8.1.1217/src/window.c 2019-04-26 20:32:57.086296530 +0200 --- src/window.c 2019-04-27 20:05:21.286555269 +0200 *************** *** 3625,3630 **** --- 3625,3632 ---- unref_var_dict(tp->tp_vars); #endif + vim_free(tp->tp_localdir); + #ifdef FEAT_PYTHON python_tabpage_free(tp); #endif *************** *** 3662,3667 **** --- 3664,3671 ---- } 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,3844 **** --- 3843,3851 ---- 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,4461 **** curwin->w_cursor.coladd = 0; changed_line_abv_curs(); /* assume cursor position needs updating */ ! if (curwin->w_localdir != NULL) { ! /* Window 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]; --- 4458,4470 ---- curwin->w_cursor.coladd = 0; changed_line_abv_curs(); /* assume cursor position needs updating */ ! if (curwin->w_localdir != NULL || curtab->tp_localdir != NULL) { ! 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,4469 **** if (mch_dirname(cwd, MAXPATHL) == OK) globaldir = vim_strsave(cwd); } ! if (mch_chdir((char *)curwin->w_localdir) == 0) shorten_fnames(TRUE); } else if (globaldir != NULL) --- 4472,4483 ---- if (mch_dirname(cwd, MAXPATHL) == OK) globaldir = vim_strsave(cwd); } ! 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) *** ../vim-8.1.1217/src/version.c 2019-04-27 19:15:41.856806819 +0200 --- src/version.c 2019-04-27 20:04:37.658747152 +0200 *************** *** 769,770 **** --- 769,772 ---- { /* Add new patch number below this line */ + /**/ + 1218, /**/ -- WOMAN: Dennis, there's some lovely filth down here. Oh -- how d'you do? ARTHUR: How do you do, good lady. I am Arthur, King of the Britons. Who's castle is that? WOMAN: King of the who? The Quest for the Holy Grail (Monty Python) /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\ \\\ an exciting new programming language -- http://www.Zimbu.org /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///