]> granicus.if.org Git - vim/commitdiff
patch 8.1.1218: cannot set a directory for a tab page v8.1.1218
authorBram Moolenaar <Bram@vim.org>
Sat, 27 Apr 2019 18:37:57 +0000 (20:37 +0200)
committerBram Moolenaar <Bram@vim.org>
Sat, 27 Apr 2019 18:37:57 +0000 (20:37 +0200)
Problem:    Cannot set a directory for a tab page.
Solution:   Add the tab-local directory. (Yegappan Lakshmanan, closes #4212)

20 files changed:
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/version.c
src/window.c

index edd1f54f211a195a5d381ec18d0d02a6e9a047d7..5d60fbeea6c555ce77cab9e94336cf102af0d6c1 100644 (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
index 1fc1c2fef84c32253c09a323f33c61e0d11651d5..60ac959b585a865fdf7f5e7b21757d94b26c0227 100644 (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
index ceaa0e0645f02d9c9dc6112d6b1ae9aed5872d6d..f728c12cbb178c951bbac924be147e509e137b6b 100644 (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)
index 83be4a529283a0a629eae7ffb3dde1cb0286f5d5..c95de5b7bb71e5a1fef814784c0b038e6eb005be 100644 (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
index 1a6e561fbb0e871c581a9f0e330d7aebd848d400..1502fc7e44ebd17ff0f941773ab5e15988f06e5f 100644 (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
index 06ab022419db401a01971494db603291821e53c9..e50237a9aac4e3b529418e67aa4b0756924ee8b4 100644 (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
 
index 429e242c87bc23800acedc740b5a6d4bce04106c..c95dca66a7436b9267a18d6f37daf8b725e079c6 100644 (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
index a1ad0e673b7e37cf18fa69112a86c0230f37a91b..0a16900262b38a46471b02e919a46e834acd6f03 100644 (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;
 }
index 58b8492e569e9927300ccaca9af852217aca1955..509a31f0e6f807024ef617478f693efec5b92ac5 100644 (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;
 }
 
 /*
index feea5b2bae13e59c4b110b15a8049629e70c2584..1531e68c1ba57d07031e81831f3d7634a4588b98 100644 (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;
index ac354cda249002a4b598f721dbf04234968f39b5..27029baf0b03a33d83a13ed1a962fa71210bfb68 100644 (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),
index 0086589342877db1c8393dcaa72e8c438eb42e3e..744469783467bc5409d2113f5e160a3e05c620c0 100644 (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.).
         */
index 20affe31db6061287ead206593a28abbd4fd9498..ede2f5cde41255760624885c034f5823e403ea96 100644 (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())
     {
index 0e6182f0168c7c6ed1dd1d406f8ddb5a743ef656..f501300a38a7ef6699a609cf7c1f44091c646c8e 100644 (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);
index 710f48b781a3bee3f1036cb09f8e248409279347..9934d60fcb8d14e083c956d5d3a1aa873119e077 100644 (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);
index 89749e5c214b2dc05b29d6e742ea84ab24c89634..40e87d550038dab530fbad917cab419fae4dfdea 100644 (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]);
index ca098781e4195e69de974bbd121f558af1b57369..50c940febee38fd17f93fbdbfadb3c858c9d965f 100644 (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
index 456cd1e4238ef1a25e27363dd3aeb3c4113c60ab..bc413968b4d4cc340076720b59a19992ab3d10ed 100644 (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
index 4c2620f291067ddb82710e0606836eb1f9a27b4f..25884c3768bb2b82860095206838c74acdbb6921 100644 (file)
@@ -767,6 +767,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1218,
 /**/
     1217,
 /**/
index b132361a5a08971be53d577d1351f64abbfa8a91..4a92d6cb50a030d95ba5b4b76f46ff5251c7d027 100644 (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)